home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0 / stk-3 / blt-for-STk-3.0 / blt-1.9 / src / bltTable.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-26  |  109.3 KB  |  3,749 lines

  1.  
  2. /*
  3.  * bltTable.c --
  4.  *
  5.  *    This module implements a table geometry manager for the
  6.  *    Tk toolkit.
  7.  *
  8.  * Copyright 1993-1994 by AT&T Bell Laboratories.
  9.  * Permission to use, copy, modify, and distribute this software
  10.  * and its documentation for any purpose and without fee is hereby
  11.  * granted, provided that the above copyright notice appear in all
  12.  * copies and that both that the copyright notice and warranty
  13.  * disclaimer appear in supporting documentation, and that the
  14.  * names of AT&T Bell Laboratories any of their entities not be used
  15.  * in advertising or publicity pertaining to distribution of the
  16.  * software without specific, written prior permission.
  17.  *
  18.  * AT&T disclaims all warranties with regard to this software, including
  19.  * all implied warranties of merchantability and fitness.  In no event
  20.  * shall AT&T be liable for any special, indirect or consequential
  21.  * damages or any damages whatsoever resulting from loss of use, data
  22.  * or profits, whether in an action of contract, negligence or other
  23.  * tortuous action, arising out of or in connection with the use or
  24.  * performance of this software.
  25.  *
  26.  * Table geometry manager created by George Howlett.
  27.  */
  28.  
  29. /*
  30.  * To do:
  31.  *
  32.  * 2) No way to detect if window is already a slave of another geometry
  33.  *    manager.
  34.  *
  35.  * 3) No way to detect if window is already a master of another geometry
  36.  *    manager.  This one is bad because is can cause a bad interaction
  37.  *    with the window manager.
  38.  *
  39.  * 5) Relative sizing of partitions?
  40.  *
  41.  * 8) Change row/column allocation procedures?
  42.  *
  43.  */
  44.  
  45. #include "blt.h"
  46. #ifdef HAVE_LIMITS_H
  47. #include <limits.h>
  48. #endif
  49. #include <X11/Xutil.h>
  50. #include <X11/Xatom.h>
  51.  
  52. #ifndef TABLE_VERSION
  53. #define TABLE_VERSION "1.1"
  54. #endif
  55.  
  56. #define TRUE     1
  57. #define FALSE     0
  58.  
  59. #ifndef ABS
  60. #define ABS(x)        (((x)<0)?(-(x)):(x))
  61. #endif /* ABS */
  62.  
  63. #ifndef SHRT_MAX
  64. #define SHRT_MAX    0x7FFF
  65. #endif /* SHRT_MAX */
  66.  
  67. #ifndef USHRT_MAX
  68. #define    USHRT_MAX    0xFFFF
  69. #endif /* USHRT_MAX */
  70.  
  71. #define NUMENTRIES(t,type) \
  72.     (((type) == ROW_PARTITION_TYPE) ? (t)->numRows : (t)->numCols)
  73.  
  74. /*
  75.  * The following enumerated values are used as bit flags.
  76.  */
  77. typedef enum {
  78.     FILL_NONE, FILL_X, FILL_Y, FILL_BOTH
  79. } FillFlags;
  80.  
  81. typedef enum {
  82.     RESIZE_NONE, RESIZE_EXPAND, RESIZE_SHRINK, RESIZE_BOTH
  83. } ResizeFlags;
  84.  
  85. static char *fillStrings[] =
  86. {
  87.     "none", "x", "y", "both"
  88. };
  89. static char *resizeStrings[] =
  90. {
  91.     "none", "expand", "shrink", "both"
  92. };
  93.  
  94. /*
  95.  * Default bounds for both partitions and slave window requested sizes.
  96.  */
  97. #define DEF_MAX_LIMIT    SHRT_MAX
  98. #define DEF_MIN_LIMIT    0
  99.  
  100. #define WITHOUT_PAD     0
  101. #define WITH_PAD    1
  102.  
  103. /*
  104.  * Default values for slave window attributes.
  105.  */
  106. #define DEF_FILL    FILL_NONE
  107. #define DEF_IPAD_X    0
  108. #define DEF_IPAD_Y    0
  109. #define DEF_PAD_X    0
  110. #define DEF_PAD_Y    0
  111. #define DEF_COLUMN_SPAN    1
  112. #define DEF_ROW_SPAN    1
  113. #define DEF_ANCHOR    TK_ANCHOR_CENTER
  114.  
  115. static int initialized = 0;
  116. static Tcl_HashTable masterWindows, slaveWindows;
  117.  
  118. /*
  119.  * Sun's bundled and unbundled C compilers choke when using function
  120.  * typedefs that are declared static (but can handle "extern") such as
  121.  *
  122.  *     static Tk_OptionParseProc parseProc;
  123.  *      static Tk_OptionPrintProc printProc;
  124.  *
  125.  * Workaround: provide the long forward declarations here.
  126. */
  127. static int ParseLimits _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  128.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  129. static char *PrintLimits _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  130.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  131.  
  132. static Tk_CustomOption LimitsOption =
  133. {
  134.     ParseLimits, PrintLimits, (ClientData)0
  135. };
  136.  
  137. static int ParseFill _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  138.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  139. static char *PrintFill _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  140.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  141.  
  142. static Tk_CustomOption FillOption =
  143. {
  144.     ParseFill, PrintFill, (ClientData)0
  145. };
  146.  
  147. static int ParseResize _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  148.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  149. static char *PrintResize _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  150.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  151.  
  152. static Tk_CustomOption ResizeOption =
  153. {
  154.     ParseResize, PrintResize, (ClientData)0
  155. };
  156.  
  157. typedef struct {
  158.     int max, min, nom;
  159. } Limits;
  160.  
  161. typedef enum {
  162.     ROW_PARTITION_TYPE, COLUMN_PARTITION_TYPE
  163. } PartitionTypes;
  164.  
  165. static char *partitionNames[] =
  166. {
  167.     "row", "column"
  168. };
  169.  
  170. /*
  171.  * The following structure is the official type record for the
  172.  * table manager:
  173.  */
  174.  
  175. static void           SlaveReqProc _ANSI_ARGS_((ClientData clientData,
  176.                           Tk_Window tkwin));
  177. static void           SlaveLostProc _ANSI_ARGS_((ClientData clientData,
  178.                           Tk_Window tkwin));
  179.  
  180. static Tk_GeomMgr TableType = {
  181.     "blt_table",                          /* name */
  182.     SlaveReqProc,                 /* requestProc */
  183.     SlaveLostProc,               /* lostSlaveProc */
  184. };
  185.  
  186. /*
  187.  * Partition --
  188.  *
  189.  *     A partition creates a definable space (row or column) in the
  190.  *    table. It may have requested minimum or maximum values which
  191.  *    constrain the size of it.
  192.  *
  193.  */
  194. typedef struct {
  195.     int size;            /* Current size of the partition. This size
  196.                  * is bounded by minSize and maxSize. */
  197.     int nomSize;        /* The nominal size (neither expanded nor
  198.                  * shrunk) of the partition based upon the
  199.                  * requested sizes of the slave windows in
  200.                  * this partition. */
  201.     int minSize, maxSize;    /* Size constraints on the partition */
  202.     int offset;            /* Offset of the partition (in pixels) from
  203.                  * the origin of the master window */
  204.     int span;            /* Minimum spanning window in partition */
  205.  
  206.     /* user-definable fields */
  207.  
  208.     ResizeFlags resize;        /* Indicates if the partition should shrink
  209.                  * or expand from its nominal size. */
  210.     int pad;            /* Pads the partition beyond its nominal
  211.                  * size */
  212.     Limits reqSize;        /* Requested bounds for the size of the
  213.                  * partition. The partition will not expand
  214.                  * or shrink beyond these limits, regardless
  215.                  * of how it was specified (max slave size).
  216.                  * This includes any extra padding which may
  217.                  * be specified. */
  218. } Partition;
  219.  
  220. #define DEF_TBL_RESIZE    "both"
  221.  
  222. static Tk_ConfigSpec rowConfigSpecs[] =
  223. {
  224.     {TK_CONFIG_CUSTOM, "-height", (char *)NULL, (char *)NULL,
  225.     (char *)NULL, Tk_Offset(Partition, reqSize), TK_CONFIG_NULL_OK,
  226.     &LimitsOption},
  227.     {TK_CONFIG_PIXELS, "-pady", (char *)NULL, (char *)NULL,
  228.     (char *)NULL, Tk_Offset(Partition, pad), 0},
  229.     {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL,
  230.     DEF_TBL_RESIZE, Tk_Offset(Partition, resize),
  231.     TK_CONFIG_DONT_SET_DEFAULT, &ResizeOption},
  232.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  233. };
  234.  
  235. static Tk_ConfigSpec columnConfigSpecs[] =
  236. {
  237.     {TK_CONFIG_PIXELS, "-padx", (char *)NULL, (char *)NULL,
  238.     (char *)NULL, Tk_Offset(Partition, pad), 0},
  239.     {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL,
  240.     DEF_TBL_RESIZE, Tk_Offset(Partition, resize),
  241.     TK_CONFIG_DONT_SET_DEFAULT, &ResizeOption},
  242.     {TK_CONFIG_CUSTOM, "-width", (char *)NULL, (char *)NULL,
  243.     (char *)NULL, Tk_Offset(Partition, reqSize), TK_CONFIG_NULL_OK,
  244.     &LimitsOption},
  245.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  246. };
  247.  
  248. static Tk_ConfigSpec *partConfigSpecs[2] =
  249. {
  250.     rowConfigSpecs, columnConfigSpecs
  251. };
  252.  
  253. struct Table;
  254.  
  255. /*
  256.  * Cubicle --
  257.  *
  258.  *     A cubicle is the frame which contains a slave window and its
  259.  *    padding. It may span multiple partitions and have requested
  260.  *    limits which constrain the size of it.  Currently, only a
  261.  *    single window can sit in a cubicle.
  262.  */
  263. typedef struct {
  264.     Tk_Window tkwin;        /* Slave window */
  265.     struct Table *tablePtr;    /* Table managing this window */
  266.     int x, y;            /* Last known position of the slave window
  267.                  * in its parent window.  Used to determine
  268.                  * if the window has moved since last
  269.                  * layout. */
  270.     int extBW;            /* Last known border width of slave window */
  271.  
  272.  
  273.     Limits reqWidth, reqHeight;    /* Configurable bounds for width and height
  274.                  * requests made by the slave window */
  275.     int rowSpan;        /* Number of rows spanned by slave */
  276.     int rowIndex;        /* Starting row of span */
  277.     int colSpan;        /* Number of columns spanned by slave */
  278.     int colIndex;        /* Starting column of span */
  279.  
  280.     Tk_Anchor anchor;        /* Anchor type: indicates how the window is
  281.                  * positioned if extra space is available in
  282.                  * the cubicle */
  283.     int padX, padY;        /* Extra padding around the slave window */
  284.     int ipadX, ipadY;        /* Extra padding inside of the slave window
  285.                  * (in addition to the requested size of
  286.                  * the window) */
  287.     FillFlags fill;        /* Fill style flag */
  288.     Blt_ListEntry *rowEntryPtr;    /* Pointer to cubicle in table's row sorted
  289.                  * list */
  290.     Blt_ListEntry *colEntryPtr;    /* Pointer to cubicle in table's column
  291.                      * sorted list */
  292. } Cubicle;
  293.  
  294. #define DEF_TBL_FILL    "none"
  295. #define DEF_TBL_ANCHOR    "center"
  296. #define DEF_TBL_SPAN    "1"
  297.  
  298. static Tk_ConfigSpec cubicleConfigSpecs[] =
  299. {
  300.     {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL,
  301.     DEF_TBL_ANCHOR, Tk_Offset(Cubicle, anchor),
  302.     TK_CONFIG_DONT_SET_DEFAULT},
  303.     {TK_CONFIG_INT, "-columnspan", "columnSpan", (char *)NULL,
  304.     DEF_TBL_SPAN, Tk_Offset(Cubicle, colSpan),
  305.     TK_CONFIG_DONT_SET_DEFAULT},
  306.     {TK_CONFIG_SYNONYM, "-cspan", "columnSpan", (char *)NULL,
  307.     (char *)NULL, Tk_Offset(Cubicle, colSpan), 0},
  308.     {TK_CONFIG_CUSTOM, "-fill", (char *)NULL, (char *)NULL,
  309.     DEF_TBL_FILL, Tk_Offset(Cubicle, fill),
  310.     TK_CONFIG_DONT_SET_DEFAULT, &FillOption},
  311.     {TK_CONFIG_PIXELS, "-padx", (char *)NULL, (char *)NULL,
  312.     (char *)NULL, Tk_Offset(Cubicle, padX), 0},
  313.     {TK_CONFIG_PIXELS, "-pady", (char *)NULL, (char *)NULL,
  314.     (char *)NULL, Tk_Offset(Cubicle, padY), 0},
  315.     {TK_CONFIG_PIXELS, "-ipadx", (char *)NULL, (char *)NULL,
  316.     (char *)NULL, Tk_Offset(Cubicle, ipadX), 0},
  317.     {TK_CONFIG_PIXELS, "-ipady", (char *)NULL, (char *)NULL,
  318.     (char *)NULL, Tk_Offset(Cubicle, ipadY), 0},
  319.     {TK_CONFIG_CUSTOM, "-reqheight", (char *)NULL, (char *)NULL,
  320.     (char *)NULL, Tk_Offset(Cubicle, reqHeight), TK_CONFIG_NULL_OK,
  321.     &LimitsOption},
  322.     {TK_CONFIG_CUSTOM, "-reqwidth", (char *)NULL, (char *)NULL,
  323.     (char *)NULL, Tk_Offset(Cubicle, reqWidth), TK_CONFIG_NULL_OK,
  324.     &LimitsOption},
  325.     {TK_CONFIG_INT, "-rowspan", "rowSpan", (char *)NULL,
  326.     DEF_TBL_SPAN, Tk_Offset(Cubicle, rowSpan),
  327.     TK_CONFIG_DONT_SET_DEFAULT},
  328.     {TK_CONFIG_SYNONYM, "-rspan", "rowSpan", (char *)NULL,
  329.     (char *)NULL, Tk_Offset(Cubicle, rowSpan), 0},
  330.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  331. };
  332.  
  333. /*
  334.  * Cubicles are stored in two linked lists in the Table structure.
  335.  * They are keyed by their starting row,column position.  The
  336.  * following union makes a one-word key out of the row and column
  337.  * components.  This, of course, assumes that a short int is half the
  338.  * size of an int.  Which is NOT the case on a cray;
  339. */
  340. typedef union {
  341.     struct Position {
  342.     unsigned short row;
  343.     unsigned short column;
  344.     } position;
  345.     unsigned int index;
  346. } SlaveKey;
  347.  
  348. /*
  349.  * This is the default number of elements in the statically
  350.  * pre-allocated column and row arrays.  This number should reflect a
  351.  * useful number of row and columns, which fit most applications.
  352. */
  353. #define DEF_ARRAY_SIZE    32
  354.  
  355. /*
  356.  * Table structure
  357.  */
  358. typedef struct Table {
  359.     int flags;            /* See the flags definitions below. */
  360.     Tk_Window searchWin;    /* Main window of window hierarchy */
  361.     Tk_Window tkwin;        /* The master window in which the slave
  362.                  * windows are arranged. */
  363.     Tcl_Interp *interp;        /* Interpreter associated with all windows */
  364.     Blt_LinkedList *listPtr;    /* Points to either list of row or column
  365.                  * sorted cubicles */
  366.     Blt_LinkedList rowSorted;    /* List of cubicles sorted by increasing
  367.                  * order of rows spanned and row,column
  368.                  * indices */
  369.     Blt_LinkedList colSorted;    /* List of cubicles sorted by increasing
  370.                  * order of columns spanned and column,row
  371.                  * indices */
  372.     /*
  373.      * Pre-allocated row and column partitions.
  374.      */
  375.     Partition colSpace[DEF_ARRAY_SIZE];
  376.     Partition rowSpace[DEF_ARRAY_SIZE];
  377.  
  378.     Partition *colPtr;        /* Pointer to array of column information:
  379.                  * Initially points to colSpace */
  380.     Partition *rowPtr;        /* Pointer to array of row information:
  381.                  * Initially points to rowSpace */
  382.     int rowSize;        /* Number of rows allocated */
  383.     int numRows;        /* Number of rows currently used */
  384.     int colSize;        /* Number of columns allocated */
  385.     int numCols;        /* Number of columns currently used */
  386.     int width, height;        /* Dimensions of the master window */
  387.     int reqWidth, reqHeight;    /* Normal width and height of table */
  388. } Table;
  389.  
  390. /*
  391.  * Table flags definitions
  392.  */
  393. #define ARRANGE_PENDING 1    /* A call to ArrangeTable is pending. This
  394.                  * flag allows multiple layout changes to be
  395.                  * requested before the table is actually
  396.                  * reconfigured. */
  397. #define REQUEST_LAYOUT     2    /* Get the requested sizes of the slave
  398.                  * windows before expanding/shrinking the
  399.                  * size of the master window.  It's
  400.                  * necessary to recompute the layout every
  401.                  * time a partition or cubicle is added,
  402.                  * reconfigured, or deleted, but not when
  403.                  * the master window is resized. */
  404. #define NON_PARENT    4    /* The table is managing slaves which are
  405.                  * not children of the master window. This
  406.                  * requires that they are moved when the
  407.                  * master window is moved (a definite
  408.                  * performance hit). */
  409. /*
  410.  * Forward declarations
  411.  */
  412. static void ArrangeTable _ANSI_ARGS_((ClientData clientData));
  413. static void DestroyTable _ANSI_ARGS_((char *blockPtr));
  414. static void DestroyCubicle _ANSI_ARGS_((Cubicle *cubiPtr));
  415. static void TableEventProc _ANSI_ARGS_((ClientData clientData,
  416.     XEvent *eventPtr));
  417. static void InitPartitions _ANSI_ARGS_((Partition *partPtr, int length));
  418. static void LinkRowEntry _ANSI_ARGS_((Cubicle *cubiPtr));
  419. static void LinkColumnEntry _ANSI_ARGS_((Cubicle *cubiPtr));
  420. static int NumEntries _ANSI_ARGS_((Table *table, PartitionTypes type));
  421.  
  422. extern int strcasecmp _ANSI_ARGS_((CONST char *s1, CONST char *s2));
  423.  
  424.  
  425. /*
  426.  *----------------------------------------------------------------------
  427.  *
  428.  * ParseLimits --
  429.  *
  430.  *    Converts the list of elements into zero or more pixel values
  431.  *    which determine the range of pixel values possible.  An element
  432.  *    can be in any form accepted by Tk_GetPixels. The list has a
  433.  *    different meaning based upon the number of elements.
  434.  *
  435.  *        # of elements:
  436.  *        0 - the limits are reset to the defaults.
  437.  *        1 - the minimum and maximum values are set to this
  438.  *            value, freezing the range at a single value.
  439.  *        2 - first element is the minimum, the second is the
  440.  *            maximum.
  441.  *        3 - first element is the minimum, the second is the
  442.  *            maximum, and the third is the nominal value.
  443.  *
  444.  * Results:
  445.  *    The return value is a standard Tcl result.  The min and
  446.  *    max fields of the range are set.
  447.  *
  448.  *----------------------------------------------------------------------
  449.  */
  450. /*ARGSUSED*/
  451. static int
  452. ParseLimits(clientData, interp, tkwin, value, widgRec, offset)
  453.     ClientData clientData;    /* not used */
  454.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  455.     Tk_Window tkwin;        /* Window of table */
  456.     char *value;        /* New width list */
  457.     char *widgRec;        /* Widget record */
  458.     int offset;            /* Offset of limits */
  459. {
  460.     Limits *limitsPtr = (Limits *)(widgRec + offset);
  461.     int numElem;
  462.     char **elemArr;
  463.     int result = TCL_ERROR;
  464.     int lim[3];
  465.     register int i;
  466.     int size;
  467.  
  468.     if (value == NULL) {
  469.     limitsPtr->nom = limitsPtr->min = DEF_MIN_LIMIT;
  470.     limitsPtr->max = DEF_MAX_LIMIT;
  471.     return TCL_OK;
  472.     }
  473.     if (Tcl_SplitList(interp, value, &numElem, &elemArr) != TCL_OK) {
  474.     return TCL_ERROR;
  475.     }
  476.     if (numElem > 3) {
  477.     Tcl_AppendResult(interp, "wrong # limits \"", value, "\"",
  478.         (char *)NULL);
  479.     goto error;
  480.     }
  481.     for (i = 0; i < numElem; i++) {
  482.     if (((elemArr[i][0] == 'i') || (elemArr[i][0] == 'I')) &&
  483.         (strcasecmp(elemArr[i], "inf") == 0)) {
  484.         size = DEF_MAX_LIMIT;
  485.     } else if (Tk_GetPixels(interp, tkwin, elemArr[i], &size) != TCL_OK) {
  486.         goto error;
  487.     }
  488.     if ((size < DEF_MIN_LIMIT) || (size > DEF_MAX_LIMIT)) {
  489.         Tcl_AppendResult(interp, "invalid limit \"", value, "\"",
  490.         (char *)NULL);
  491.         goto error;
  492.     }
  493.     lim[i] = size;
  494.     }
  495.     switch (numElem) {
  496.     case 1:
  497.     limitsPtr->max = limitsPtr->min = lim[0];
  498.     limitsPtr->nom = DEF_MIN_LIMIT;
  499.     break;
  500.     case 2:
  501.     if (lim[1] < lim[0]) {
  502.         Tcl_AppendResult(interp, "invalid limits \"", value, "\"",
  503.         (char *)NULL);
  504.         goto error;
  505.     }
  506.     limitsPtr->min = lim[0];
  507.     limitsPtr->max = lim[1];
  508.     limitsPtr->nom = DEF_MIN_LIMIT;
  509.     break;
  510.     case 3:
  511.     if (lim[1] < lim[0]) {
  512.         Tcl_AppendResult(interp, "invalid range \"", value, "\"",
  513.         (char *)NULL);
  514.         goto error;
  515.     }
  516.     if ((lim[2] < lim[0]) || (lim[2] > lim[1])) {
  517.         Tcl_AppendResult(interp, "invalid nominal \"", value, "\"",
  518.         (char *)NULL);
  519.         goto error;
  520.     }
  521.     limitsPtr->min = lim[0];
  522.     limitsPtr->max = lim[1];
  523.     limitsPtr->nom = lim[2];
  524.     break;
  525.     }
  526.     result = TCL_OK;
  527.   error:
  528.     ckfree(elemArr);
  529.     return result;
  530. }
  531.  
  532.  
  533. static void
  534. LimitsToString(min, max, nom, string)
  535.     int min, max, nom;
  536.     char string[];
  537. {
  538.     if (nom > DEF_MIN_LIMIT) {
  539.     sprintf(string, "%d %d %d", min, max, nom);
  540.     } else if (min == max) {
  541.     sprintf(string, "%d", max);
  542.     } else if ((min != DEF_MIN_LIMIT) || (max != DEF_MAX_LIMIT)) {
  543.     sprintf(string, "%d %d", min, max);
  544.     } else {
  545.     string[0] = '\0';
  546.     }
  547. }
  548.  
  549. /*
  550.  *----------------------------------------------------------------------
  551.  *
  552.  * PrintLimits --
  553.  *
  554.  *    Convert the limits of the pixel values allowed into a list.
  555.  *
  556.  * Results:
  557.  *    The string representation of the limits is returned.
  558.  *
  559.  *----------------------------------------------------------------------
  560.  */
  561. /*ARGSUSED*/
  562. static char *
  563. PrintLimits(clientData, tkwin, widgRec, offset, freeProcPtr)
  564.     ClientData clientData;    /* not used */
  565.     Tk_Window tkwin;        /* not used */
  566.     char *widgRec;        /* Row/column structure record */
  567.     int offset;            /* Offset of window Partition record */
  568.     Tcl_FreeProc **freeProcPtr;    /* Memory deallocation routine */
  569. {
  570.     Limits *limitsPtr = (Limits *)(widgRec + offset);
  571.     char *result;
  572.     char string[200];
  573.  
  574.     LimitsToString(limitsPtr->min, limitsPtr->max, limitsPtr->nom, string);
  575.     result = strdup(string);
  576.     *freeProcPtr = TCL_DYNAMIC;
  577.     return (result);
  578. }
  579.  
  580. /*
  581.  *----------------------------------------------------------------------
  582.  *
  583.  * ParseFill --
  584.  *
  585.  *    Converts the fill style string into its numeric representation.
  586.  *    This configuration option affects how the slave window is expanded
  587.  *    if there is extra space in the cubicle in which it sits.
  588.  *
  589.  *    Valid style strings are:
  590.  *
  591.  *        "none"     Don't expand the window to fill the cubicle.
  592.  *         "x"     Expand only the window's width.
  593.  *        "y"     Expand only the window's height.
  594.  *        "both"   Expand both the window's height and width.
  595.  *
  596.  *----------------------------------------------------------------------
  597.  */
  598. /*ARGSUSED*/
  599. static int
  600. ParseFill(clientData, interp, tkwin, value, widgRec, offset)
  601.     ClientData clientData;    /* not used */
  602.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  603.     Tk_Window tkwin;        /* not used */
  604.     char *value;        /* Fill style string */
  605.     char *widgRec;        /* Cubicle structure record */
  606.     int offset;            /* Offset of style in record */
  607. {
  608.     FillFlags *fillPtr = (FillFlags *)(widgRec + offset);
  609.     int length;
  610.     char c;
  611.  
  612.     c = value[0];
  613.     length = strlen(value);
  614.     if ((c == 'n') && (strncmp(value, "none", length) == 0)) {
  615.     *fillPtr = FILL_NONE;
  616.     } else if ((c == 'x') && (strncmp(value, "x", length) == 0)) {
  617.     *fillPtr = FILL_X;
  618.     } else if ((c == 'y') && (strncmp(value, "y", length) == 0)) {
  619.     *fillPtr = FILL_Y;
  620.     } else if ((c == 'b') && (strncmp(value, "both", length) == 0)) {
  621.     *fillPtr = FILL_BOTH;
  622.     } else {
  623.     Tcl_AppendResult(interp, "bad fill argument \"", value,
  624.         "\": should be none, x, y, or both", (char *)NULL);
  625.     return TCL_ERROR;
  626.     }
  627.     return (TCL_OK);
  628. }
  629.  
  630. /*
  631.  *----------------------------------------------------------------------
  632.  *
  633.  * PrintFill --
  634.  *
  635.  *    Returns the fill style string based upon the fill flags.
  636.  *
  637.  * Results:
  638.  *    The fill style string is returned.
  639.  *
  640.  *----------------------------------------------------------------------
  641.  */
  642. /*ARGSUSED*/
  643. static char *
  644. PrintFill(clientData, tkwin, widgRec, offset, freeProcPtr)
  645.     ClientData clientData;    /* not used */
  646.     Tk_Window tkwin;        /* not used */
  647.     char *widgRec;        /* Row/column structure record */
  648.     int offset;            /* Offset of fill in Partition record */
  649.     Tcl_FreeProc **freeProcPtr;    /* not used */
  650. {
  651.     FillFlags fill = *(FillFlags *)(widgRec + offset);
  652.  
  653.     return (fillStrings[(int)fill]);
  654. }
  655.  
  656. /*
  657.  *----------------------------------------------------------------------
  658.  *
  659.  * ParseResize --
  660.  *
  661.  *    Converts the resize mode into its numeric representation.
  662.  *    Valid mode strings are "none", "expand", "shrink", or "both".
  663.  *
  664.  *----------------------------------------------------------------------
  665.  */
  666. /*ARGSUSED*/
  667. static int
  668. ParseResize(clientData, interp, tkwin, value, widgRec, offset)
  669.     ClientData clientData;    /* not used */
  670.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  671.     Tk_Window tkwin;        /* not used */
  672.     char *value;        /* Resize style string */
  673.     char *widgRec;        /* Cubicle structure record */
  674.     int offset;            /* Offset of style in record */
  675. {
  676.     ResizeFlags *resizePtr = (ResizeFlags *)(widgRec + offset);
  677.     int length;
  678.     char c;
  679.  
  680.     c = value[0];
  681.     length = strlen(value);
  682.     if ((c == 'n') && (strncmp(value, "none", length) == 0)) {
  683.     *resizePtr = RESIZE_NONE;
  684.     } else if ((c == 'b') && (strncmp(value, "both", length) == 0)) {
  685.     *resizePtr = RESIZE_BOTH;
  686.     } else if ((c == 'e') && (strncmp(value, "expand", length) == 0)) {
  687.     *resizePtr = RESIZE_EXPAND;
  688.     } else if ((c == 's') && (strncmp(value, "shrink", length) == 0)) {
  689.     *resizePtr = RESIZE_SHRINK;
  690.     } else {
  691.     Tcl_AppendResult(interp, "bad resize argument \"", value,
  692.         "\": should be none, expand, shrink, or both", (char *)NULL);
  693.     return TCL_ERROR;
  694.     }
  695.     return (TCL_OK);
  696. }
  697.  
  698. /*
  699.  *----------------------------------------------------------------------
  700.  *
  701.  * PrintResize --
  702.  *
  703.  *    Returns resize mode string based upon the resize flags.
  704.  *
  705.  * Results:
  706.  *    The resize mode string is returned.
  707.  *
  708.  *----------------------------------------------------------------------
  709.  */
  710. /*ARGSUSED*/
  711. static char *
  712. PrintResize(clientData, tkwin, widgRec, offset, freeProcPtr)
  713.     ClientData clientData;    /* not used */
  714.     Tk_Window tkwin;        /* not used */
  715.     char *widgRec;        /* Row/column structure record */
  716.     int offset;            /* Offset of resize in Partition record */
  717.     Tcl_FreeProc **freeProcPtr;    /* not used */
  718. {
  719.     ResizeFlags resize = *(ResizeFlags *)(widgRec + offset);
  720.  
  721.     return (resizeStrings[(int)resize]);
  722. }
  723.  
  724. /*
  725.  *--------------------------------------------------------------
  726.  *
  727.  * TableEventProc --
  728.  *
  729.  *    This procedure is invoked by the Tk event handler when
  730.  *    the master window is reconfigured or destroyed.
  731.  *
  732.  *    The table will be rearranged at the next idle point if
  733.  *    the master window has been resized or moved. There's a
  734.  *    distinction made between parent and non-parent master
  735.  *    window arrangements.  If the master window is moved and
  736.  *    it's the parent of its slaves, the slaves are moved
  737.  *    automatically.  If it's not the parent, the slaves need
  738.  *    to be moved.  This can be a performance hit in rare cases
  739.  *    where we're scrolling the master window (by moving it)
  740.  *    and there are lots of slave windows.
  741.  *
  742.  * Results:
  743.  *    None.
  744.  *
  745.  * Side effects:
  746.  *    Arranges for the table associated with tkwin to have its
  747.  *    layout re-computed and drawn at the next idle point.
  748.  *
  749.  *--------------------------------------------------------------
  750.  */
  751. static void
  752. TableEventProc(clientData, eventPtr)
  753.     ClientData clientData;    /* Information about window */
  754.     XEvent *eventPtr;        /* Information about event */
  755. {
  756.     register Table *tablePtr = (Table *)clientData;
  757.  
  758.     if (eventPtr->type == ConfigureNotify) {
  759.     if ((Blt_GetListLength(tablePtr->listPtr) > 0) &&
  760.         !(tablePtr->flags & ARRANGE_PENDING) &&
  761.         ((tablePtr->width != Tk_Width(tablePtr->tkwin)) ||
  762.         (tablePtr->height != Tk_Height(tablePtr->tkwin))
  763.         || (tablePtr->flags & NON_PARENT))) {
  764.         tablePtr->flags |= ARRANGE_PENDING;
  765.         Tk_DoWhenIdle(ArrangeTable, (ClientData)tablePtr);
  766.     }
  767.     } else if (eventPtr->type == DestroyNotify) {
  768.     if (tablePtr->flags & ARRANGE_PENDING) {
  769.         Tk_CancelIdleCall(ArrangeTable, (ClientData)tablePtr);
  770.     }
  771.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&masterWindows,
  772.         (char *)tablePtr->tkwin));
  773.     tablePtr->tkwin = NULL;
  774. #if (TK_MINOR_VERSION > 0)
  775.     Tk_EventuallyFree((ClientData)tablePtr,(Tcl_FreeProc *) DestroyTable);
  776. #else
  777.     Tk_EventuallyFree((ClientData)tablePtr,(Tk_FreeProc *) DestroyTable);
  778. #endif
  779.     }
  780. }
  781.  
  782. /*
  783.  *----------------------------------------------------------------------
  784.  *
  785.  * SlaveEventProc --
  786.  *
  787.  *    This procedure is invoked by the Tk event handler when
  788.  *    StructureNotify events occur for a slave window.  When a slave
  789.  *    window is destroyed, it frees the corresponding cubicle structure
  790.  *    and arranges for the table layout to be re-computed at the next
  791.  *    idle point.
  792.  *
  793.  * Results:
  794.  *    None.
  795.  *
  796.  * Side effects:
  797.  *    If the slave window was deleted, the Cubicle structure gets cleaned
  798.  *    up and the table is rearranged.
  799.  *
  800.  *----------------------------------------------------------------------
  801.  */
  802.  
  803. static void
  804. SlaveEventProc(clientData, eventPtr)
  805.     ClientData clientData;    /* Pointer to Slave structure for window
  806.                  * referred to by eventPtr. */
  807.     XEvent *eventPtr;        /* Describes what just happened. */
  808. {
  809.     Cubicle *cubiPtr = (Cubicle *)clientData;
  810.  
  811.     if (eventPtr->type == ConfigureNotify) {
  812.     int extBW;
  813.  
  814.     extBW = Tk_Changes(cubiPtr->tkwin)->border_width;
  815.     cubiPtr->tablePtr->flags |= REQUEST_LAYOUT;
  816.     if (!(cubiPtr->tablePtr->flags & ARRANGE_PENDING) &&
  817.         (cubiPtr->extBW != extBW)) {
  818.         cubiPtr->extBW = extBW;
  819.         cubiPtr->tablePtr->flags |= ARRANGE_PENDING;
  820.         Tk_DoWhenIdle(ArrangeTable, (ClientData)cubiPtr->tablePtr);
  821.     }
  822.     } else if (eventPtr->type == DestroyNotify) {
  823.     cubiPtr->tablePtr->flags |= REQUEST_LAYOUT;
  824.     if (!(cubiPtr->tablePtr->flags & ARRANGE_PENDING)) {
  825.         cubiPtr->tablePtr->flags |= ARRANGE_PENDING;
  826.         Tk_DoWhenIdle(ArrangeTable, (ClientData)cubiPtr->tablePtr);
  827.     }
  828.     DestroyCubicle(cubiPtr);
  829.     }
  830. }
  831.  
  832. /*
  833.  *--------------------------------------------------------------
  834.  *
  835.  * SlaveLostProc --
  836.  *
  837.  *    This procedure is invoked by Tk_GeometryRequest for slave
  838.  *    windows no longermanaged by the table geometry manager.
  839.  *
  840.  * Results:
  841.  *    None.
  842.  *
  843.  * Side effects:
  844.  *    Arranges for the table associated with the slave window to
  845.  *    forget about this slave, and free up the associated space,
  846.  *
  847.  *--------------------------------------------------------------
  848.  */
  849. /* ARGSUSED */
  850. static void
  851. SlaveLostProc(clientData, tkwin)
  852.     ClientData clientData;    /* Information about window that got new
  853.                  * preferred geometry.  */
  854.     Tk_Window tkwin;        /* Other Tk-related information about the
  855.                      * window. */
  856. {
  857.     /* this code copied from ForgetWindow (sau) */
  858.  
  859.     Cubicle *cubiPtr = (Cubicle *)clientData;
  860.  
  861.     if (Tk_IsMapped(cubiPtr->tkwin)) {
  862.     Tk_UnmapWindow(cubiPtr->tkwin);
  863.     }
  864.     /*
  865.      * Arrange for the call back here because not all the named
  866.      * slave windows may belong to the same table.
  867.      */
  868.     cubiPtr->tablePtr->flags |= REQUEST_LAYOUT;
  869.     if (!(cubiPtr->tablePtr->flags & ARRANGE_PENDING)) {
  870.     cubiPtr->tablePtr->flags |= ARRANGE_PENDING;
  871.     Tk_DoWhenIdle(ArrangeTable, (ClientData)cubiPtr->tablePtr);
  872.     }
  873.     DestroyCubicle(cubiPtr);
  874. }
  875.  
  876. /*
  877.  *--------------------------------------------------------------
  878.  *
  879.  * SlaveReqProc --
  880.  *
  881.  *    This procedure is invoked by Tk_GeometryRequest for slave
  882.  *    windows managed by the table geometry manager.
  883.  *
  884.  * Results:
  885.  *    None.
  886.  *
  887.  * Side effects:
  888.  *    Arranges for the table associated with the slave window to
  889.  *    have its layout re-computed and arranged at the next idle
  890.  *    point.
  891.  *
  892.  *--------------------------------------------------------------
  893.  */
  894. /* ARGSUSED */
  895. static void
  896. SlaveReqProc(clientData, tkwin)
  897.     ClientData clientData;    /* Information about window that got new
  898.                  * preferred geometry.  */
  899.     Tk_Window tkwin;        /* Other Tk-related information about the
  900.                      * window. */
  901. {
  902.     Cubicle *cubiPtr = (Cubicle *)clientData;
  903.  
  904.     cubiPtr->tablePtr->flags |= REQUEST_LAYOUT;
  905.     if (!(cubiPtr->tablePtr->flags & ARRANGE_PENDING)) {
  906.     cubiPtr->tablePtr->flags |= ARRANGE_PENDING;
  907.     Tk_DoWhenIdle(ArrangeTable, (ClientData)cubiPtr->tablePtr);
  908.     }
  909. }
  910.  
  911. /*
  912.  *----------------------------------------------------------------------
  913.  *
  914.  * FindCubicle --
  915.  *
  916.  *    Searches for the Cubicle structure corresponding to the given
  917.  *    window.
  918.  *
  919.  * Results:
  920.  *    If a structure associated with the window exists, a pointer to
  921.  *    that structure is returned. Otherwise NULL is returned and if
  922.  *    the TCL_LEAVE_ERR_MSG flag is set, an error message is left in
  923.  *    interp->result.
  924.  *
  925.  *----------------------------------------------------------------------
  926.  */
  927.  
  928. static Cubicle *
  929. FindCubicle(interp, tkwin, flags)
  930.     Tcl_Interp *interp;
  931.     Tk_Window tkwin;        /* Slave window associated with table entry */
  932.     int flags;
  933. {
  934.     Tcl_HashEntry *entryPtr;
  935.  
  936.     entryPtr = Tcl_FindHashEntry(&slaveWindows, (char *)tkwin);
  937.     if (entryPtr == NULL) {
  938.     if (flags & TCL_LEAVE_ERR_MSG) {
  939.         Tcl_AppendResult(interp, "\"", Tk_PathName(tkwin),
  940.         "\" is not managed by any table", (char *)NULL);
  941.     }
  942.     return NULL;
  943.     }
  944.     return ((Cubicle *)Tcl_GetHashValue(entryPtr));
  945. }
  946.  
  947. /*
  948.  *----------------------------------------------------------------------
  949.  *
  950.  * CreateCubicle --
  951.  *
  952.  *    This procedure creates and initializes a new Cubicle structure
  953.  *    to contain a slave window.  A valid slave window must have a
  954.  *    parent window that is either a) the master window or b) a mutual
  955.  *    ancestor of the master window.
  956.  *
  957.  * Results:
  958.  *    Returns a pointer to the new structure describing the new table
  959.  *    slave window entry.  If an error occurred, then the return
  960.  *    value is NULL and an error message is left in interp->result.
  961.  *
  962.  * Side effects:
  963.  *    Memory is allocated and initialized for the Cubicle structure.
  964.  *
  965.  *----------------------------------------------------------------------
  966.  */
  967. static Cubicle *
  968. CreateCubicle(tablePtr, tkwin)
  969.     Table *tablePtr;
  970.     Tk_Window tkwin;
  971. {
  972.     register Cubicle *cubiPtr;
  973.     int dummy;
  974.     Tk_Window parent, ancestor;
  975.     Tcl_HashEntry *entryPtr;
  976.     int notParent = FALSE;    /* Indicates that the master window is not the
  977.                  * parent of the new slave window. */
  978.  
  979.     /*
  980.      * A valid slave window has a parent window that either
  981.      *
  982.      *    1) is the master window, or
  983.      *       2) is a mutual ancestor of the master window.
  984.      */
  985.     ancestor = Tk_Parent(tkwin);
  986.     for (parent = tablePtr->tkwin; (parent != ancestor) &&
  987.     (!Tk_IsTopLevel(parent)); parent = Tk_Parent(parent)) {
  988.     notParent = TRUE;
  989.     }
  990.     if (ancestor != parent) {
  991.     Tcl_AppendResult(tablePtr->interp, "can't manage \"",
  992.         Tk_PathName(tkwin), "\" in table \"",
  993.         Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
  994.     return NULL;
  995.     }
  996.     cubiPtr = (Cubicle *)ckalloc(sizeof(Cubicle));
  997.     if (cubiPtr == NULL) {
  998.     tablePtr->interp->result = "can't allocate cubicle";
  999.     return NULL;
  1000.     }
  1001.     if (notParent) {
  1002.     tablePtr->flags |= NON_PARENT;
  1003.     }
  1004.     /* Initialize the cubicle structure */
  1005.  
  1006.     cubiPtr->x = cubiPtr->y = 0;
  1007.     cubiPtr->tkwin = tkwin;
  1008.     cubiPtr->tablePtr = tablePtr;
  1009.     cubiPtr->extBW = Tk_Changes(tkwin)->border_width;
  1010.     cubiPtr->fill = DEF_FILL;
  1011.     cubiPtr->ipadX = DEF_IPAD_X;
  1012.     cubiPtr->ipadY = DEF_IPAD_Y;
  1013.     cubiPtr->padX = DEF_PAD_X;
  1014.     cubiPtr->padY = DEF_PAD_Y;
  1015.     cubiPtr->anchor = DEF_ANCHOR;
  1016.     cubiPtr->rowSpan = DEF_ROW_SPAN;
  1017.     cubiPtr->colSpan = DEF_COLUMN_SPAN;
  1018.     cubiPtr->reqWidth.min = cubiPtr->reqHeight.min = DEF_MIN_LIMIT;
  1019.     cubiPtr->reqWidth.max = cubiPtr->reqHeight.max = DEF_MAX_LIMIT;
  1020.     cubiPtr->reqWidth.nom = cubiPtr->reqHeight.nom = DEF_MIN_LIMIT;
  1021.     cubiPtr->rowEntryPtr = cubiPtr->colEntryPtr = NULL;
  1022.     entryPtr = Tcl_CreateHashEntry(&slaveWindows, (char *)tkwin, &dummy);
  1023.     Tcl_SetHashValue(entryPtr, (char *)cubiPtr);
  1024.     Tk_CreateEventHandler(tkwin, StructureNotifyMask, SlaveEventProc,
  1025.     (ClientData)cubiPtr);
  1026.     Tk_ManageGeometry(tkwin, &TableType, (ClientData)cubiPtr);
  1027.     return (cubiPtr);
  1028. }
  1029.  
  1030. /*
  1031.  *----------------------------------------------------------------------
  1032.  *
  1033.  * DestroyCubicle --
  1034.  *
  1035.  *    Removes the Cubicle structure from the hash table and frees
  1036.  *    the memory allocated by it.  If the table is still in use
  1037.  *    (i.e. was not called from DestoryTable), remove its entries
  1038.  *    from the lists of row and column sorted partitions.
  1039.  *
  1040.  * Results:
  1041.  *    None.
  1042.  *
  1043.  * Side effects:
  1044.  *    Everything associated with the cubicle is freed up.
  1045.  *
  1046.  *----------------------------------------------------------------------
  1047.  */
  1048. static void
  1049. DestroyCubicle(cubiPtr)
  1050.     Cubicle *cubiPtr;
  1051. {
  1052.     Tcl_HashEntry *entryPtr;
  1053.  
  1054.     if (cubiPtr->rowEntryPtr != NULL) {
  1055.     Blt_DeleteListEntry(&(cubiPtr->tablePtr->rowSorted),
  1056.         cubiPtr->rowEntryPtr);
  1057.     }
  1058.     if (cubiPtr->colEntryPtr != NULL) {
  1059.     Blt_DeleteListEntry(&(cubiPtr->tablePtr->colSorted),
  1060.         cubiPtr->colEntryPtr);
  1061.     }
  1062.  
  1063.     Tk_DeleteEventHandler(cubiPtr->tkwin, StructureNotifyMask,
  1064.     SlaveEventProc, (ClientData)cubiPtr);
  1065.     Tk_ManageGeometry(cubiPtr->tkwin, (Tk_GeomMgr *) NULL,
  1066.     (ClientData)cubiPtr);
  1067.     entryPtr = Tcl_FindHashEntry(&slaveWindows, (char *)cubiPtr->tkwin);
  1068.     Tcl_DeleteHashEntry(entryPtr);
  1069.     ckfree((char *)cubiPtr);
  1070. }
  1071.  
  1072. /*
  1073.  *----------------------------------------------------------------------
  1074.  *
  1075.  * ConfigureCubicle --
  1076.  *
  1077.  *    This procedure is called to process an argv/argc list, plus
  1078.  *    the Tk option database, in order to configure (or reconfigure)
  1079.  *    one or more cubicles associated with a slave window which is
  1080.  *    managed by the table geometry manager.
  1081.  *
  1082.  * Note:
  1083.  *    Currently only the one slave window can be queried while many
  1084.  *    can be reconfigured at a time.
  1085.  *
  1086.  * Results:
  1087.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  1088.  *    returned, then interp->result contains an error message.
  1089.  *
  1090.  * Side effects:
  1091.  *    The table layout is recomputed and rearranged at the next
  1092.  *    idle point.
  1093.  *
  1094.  *----------------------------------------------------------------------
  1095.  */
  1096. static int
  1097. ConfigureCubicle(clientData, interp, argc, argv)
  1098.     ClientData clientData;
  1099.     Tcl_Interp *interp;
  1100.     int argc;
  1101.     char **argv;
  1102. {
  1103.     Tk_Window searchWin = (Tk_Window)clientData;
  1104.     Cubicle *cubiPtr;
  1105.     Tk_Window tkwin;
  1106.     Table *tablePtr;
  1107.     int numSlaves, numOptions;
  1108.     int oldRowSpan, oldColSpan;
  1109.     register int i;
  1110.  
  1111.     /* Find the number of slave windows to be configured */
  1112.     numSlaves = 0;
  1113.     for (i = 0; i < argc; i++) {
  1114.     if (argv[i][0] != '.') {
  1115.         break;
  1116.     }
  1117.     numSlaves++;
  1118.     }
  1119.     /* And the number of options to be applied */
  1120.     numOptions = argc - numSlaves;
  1121.  
  1122.     for (i = 0; i < numSlaves; i++) {
  1123.     tkwin = Tk_NameToWindow(interp, argv[i], searchWin);
  1124.     if (tkwin == NULL) {
  1125.         return TCL_ERROR;
  1126.     }
  1127.     cubiPtr = FindCubicle(interp, tkwin, TCL_LEAVE_ERR_MSG);
  1128.     if (cubiPtr == NULL) {
  1129.         return TCL_ERROR;
  1130.     }
  1131.     if (numOptions == 0) {
  1132.         return (Tk_ConfigureInfo(interp, tkwin, cubicleConfigSpecs,
  1133.             (char *)cubiPtr, (char *)NULL, 0));
  1134.     } else if (numOptions == 1) {
  1135.         return (Tk_ConfigureInfo(interp, tkwin, cubicleConfigSpecs,
  1136.             (char *)cubiPtr, argv[numSlaves], 0));
  1137.     }
  1138.     oldRowSpan = cubiPtr->rowSpan;
  1139.     oldColSpan = cubiPtr->colSpan;
  1140.     if (Tk_ConfigureWidget(interp, tkwin, cubicleConfigSpecs,
  1141.         numOptions, argv + numSlaves, (char *)cubiPtr,
  1142.         TK_CONFIG_ARGV_ONLY) != TCL_OK) {
  1143.         return TCL_ERROR;
  1144.     }
  1145.     if ((cubiPtr->colSpan < 1) || (cubiPtr->colSpan > USHRT_MAX)) {
  1146.         Tcl_AppendResult(interp, "bad column span specified for \"",
  1147.         Tk_PathName(tkwin), "\"", (char *)NULL);
  1148.         return TCL_ERROR;
  1149.     }
  1150.     if ((cubiPtr->rowSpan < 1) || (cubiPtr->rowSpan > USHRT_MAX)) {
  1151.         Tcl_AppendResult(interp, "bad row span specified for \"",
  1152.         Tk_PathName(tkwin), "\"", (char *)NULL);
  1153.         return TCL_ERROR;
  1154.     }
  1155.     tablePtr = cubiPtr->tablePtr;
  1156.     if (oldColSpan != cubiPtr->colSpan) {
  1157.         Blt_UnlinkListEntry(&(tablePtr->colSorted), cubiPtr->colEntryPtr);
  1158.         LinkColumnEntry(cubiPtr);
  1159.     }
  1160.     if (oldRowSpan != cubiPtr->rowSpan) {
  1161.         Blt_UnlinkListEntry(&(tablePtr->rowSorted), cubiPtr->rowEntryPtr);
  1162.         LinkRowEntry(cubiPtr);
  1163.     }
  1164.     tablePtr->flags |= REQUEST_LAYOUT;
  1165.     if (!(tablePtr->flags & ARRANGE_PENDING)) {
  1166.         tablePtr->flags |= ARRANGE_PENDING;
  1167.         Tk_DoWhenIdle(ArrangeTable, (ClientData)tablePtr);
  1168.     }
  1169.     }
  1170.     return TCL_OK;
  1171. }
  1172.  
  1173. /*
  1174.  *----------------------------------------------------------------------
  1175.  *
  1176.  * FindTable --
  1177.  *
  1178.  *    Searches for a table associated with the window given by its
  1179.  *    pathname.  This window represents the master window of the table.
  1180.  *    Errors may occur because 1) pathname does not represent a valid
  1181.  *    Tk window or 2) the window is not associated with any table
  1182.  *    as its master window.
  1183.  *
  1184.  * Results:
  1185.  *    If a table entry exists, a pointer to the Table structure is
  1186.  *    returned. Otherwise NULL is returned and if the TCL_LEAVE_ERR_MSG
  1187.  *    flag is set, an error message is left is interp->result.
  1188.  *
  1189.  *----------------------------------------------------------------------
  1190.  */
  1191. static Table *
  1192. FindTable(interp, pathName, searchWin, flags)
  1193.     Tcl_Interp *interp;        /* Interpreter to report errors back to */
  1194.     char *pathName;        /* Path name of the master window */
  1195.     Tk_Window searchWin;    /* Main window: used to search for window
  1196.                  * associated with pathname */
  1197.     int flags;            /* If non-zero, don't reset interp->result */
  1198. {
  1199.     Tcl_HashEntry *entryPtr;
  1200.     Tk_Window tkwin;
  1201.  
  1202.     tkwin = Tk_NameToWindow(interp, pathName, searchWin);
  1203.     if (tkwin == NULL) {
  1204.     if (!(flags & TCL_LEAVE_ERR_MSG)) {
  1205.         Tcl_ResetResult(interp);
  1206.     }
  1207.     return NULL;
  1208.     }
  1209.     entryPtr = Tcl_FindHashEntry(&masterWindows, (char *)tkwin);
  1210.     if (entryPtr == NULL) {
  1211.     if (flags & TCL_LEAVE_ERR_MSG) {
  1212.         Tcl_AppendResult(interp, "no table associated with window \"",
  1213.         pathName, "\"", (char *)NULL);
  1214.     }
  1215.     return NULL;
  1216.     }
  1217.     return ((Table *)Tcl_GetHashValue(entryPtr));
  1218. }
  1219.  
  1220. /*
  1221.  *----------------------------------------------------------------------
  1222.  *
  1223.  * CreateTable --
  1224.  *
  1225.  *    This procedure creates and initializes a new Table structure
  1226.  *    with tkwin as its master window. The internal structures
  1227.  *    associated with the table are initialized.
  1228.  
  1229.  * Results:
  1230.  *    Returns the pointer to the new Table structure describing the
  1231.  *    new table geometry manager.  If an error occurred, the return
  1232.  *    value will be NULL and an error message is left in interp->result.
  1233.  *
  1234.  * Side effects:
  1235.  *    Memory is allocated and initialized, an event handler is set up
  1236.  *    to watch tkwin, etc.
  1237.  *
  1238.  *----------------------------------------------------------------------
  1239.  */
  1240. static Table *
  1241. CreateTable(interp, pathName, searchWin)
  1242.     Tcl_Interp *interp;        /* Interpreter associated with table */
  1243.     char *pathName;        /* Path name of the master window to be
  1244.                  * associated with the new table */
  1245.     Tk_Window searchWin;    /* Main window */
  1246. {
  1247.     register Table *tablePtr;
  1248.     Tk_Window tkwin;
  1249.  
  1250.     tkwin = Tk_NameToWindow(interp, pathName, searchWin);
  1251.     if (tkwin == NULL) {
  1252.     return NULL;
  1253.     }
  1254.     tablePtr = (Table *)calloc(1, sizeof(Table));
  1255.     if (tablePtr != NULL) {
  1256.     int dummy;
  1257.     Tcl_HashEntry *entryPtr;
  1258.  
  1259.     tablePtr->tkwin = tkwin;
  1260.     tablePtr->searchWin = searchWin;
  1261.     tablePtr->interp = interp;
  1262.     tablePtr->listPtr = &(tablePtr->rowSorted);
  1263.     tablePtr->flags = 0;
  1264.     tablePtr->rowSize = tablePtr->colSize = DEF_ARRAY_SIZE;
  1265.     tablePtr->numRows = tablePtr->numCols = 0;
  1266.     tablePtr->rowPtr = tablePtr->rowSpace;
  1267.     Blt_InitLinkedList(&(tablePtr->rowSorted), TCL_ONE_WORD_KEYS);
  1268.     InitPartitions(tablePtr->rowPtr, DEF_ARRAY_SIZE);
  1269.  
  1270.     tablePtr->colPtr = tablePtr->colSpace;
  1271.     Blt_InitLinkedList(&(tablePtr->colSorted), TCL_ONE_WORD_KEYS);
  1272.     InitPartitions(tablePtr->colPtr, DEF_ARRAY_SIZE);
  1273.  
  1274.     Tk_CreateEventHandler(tablePtr->tkwin, StructureNotifyMask,
  1275.         TableEventProc, (ClientData)tablePtr);
  1276.     entryPtr = Tcl_CreateHashEntry(&masterWindows, (char *)tkwin, &dummy);
  1277.     Tcl_SetHashValue(entryPtr, (ClientData)tablePtr);
  1278.     } else {
  1279.     Tcl_AppendResult(interp, "can't create table \"", pathName, "\"",
  1280.         (char *)NULL);
  1281.     }
  1282.     return (tablePtr);
  1283. }
  1284.  
  1285. /*
  1286.  *----------------------------------------------------------------------
  1287.  *
  1288.  * DestroyTable --
  1289.  *
  1290.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  1291.  *    to clean up the Table structure at a safe time (when no-one is
  1292.  *    using it anymore).
  1293.  *
  1294.  * Results:
  1295.  *    None.
  1296.  *
  1297.  * Side effects:
  1298.  *    Everything associated with the table geometry manager is freed up.
  1299.  *
  1300.  *----------------------------------------------------------------------
  1301.  */
  1302. static void
  1303. DestroyTable(blockPtr)
  1304.     char *blockPtr;
  1305. {
  1306.     register Table *tablePtr = (Table *)blockPtr;
  1307.     Blt_ListEntry *entryPtr;
  1308.     Cubicle *cubiPtr;
  1309.  
  1310.     for (entryPtr = Blt_FirstListEntry(tablePtr->listPtr);
  1311.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1312.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  1313.     cubiPtr->rowEntryPtr = cubiPtr->colEntryPtr = NULL;
  1314.     DestroyCubicle(cubiPtr);
  1315.     }
  1316.     Blt_ClearList(&(tablePtr->rowSorted));
  1317.     Blt_ClearList(&(tablePtr->colSorted));
  1318.  
  1319.     if ((tablePtr->rowPtr != NULL) &&
  1320.     (tablePtr->rowPtr != tablePtr->rowSpace)) {
  1321.     ckfree((char *)tablePtr->rowPtr);
  1322.     }
  1323.     if ((tablePtr->colPtr != NULL) &&
  1324.     (tablePtr->colPtr != tablePtr->colSpace)) {
  1325.     ckfree((char *)tablePtr->colPtr);
  1326.     }
  1327.     ckfree((char *)tablePtr);
  1328. }
  1329.  
  1330. /*
  1331.  *----------------------------------------------------------------------
  1332.  *
  1333.  * InitPartitions --
  1334.  *
  1335.  *    Initializes the values of the newly created elements in
  1336.  *    the partition array.
  1337.  *
  1338.  * Results:
  1339.  *    None.
  1340.  *
  1341.  * Side effects:
  1342.  *    The elements of the array of Partition structures is
  1343.  *    initialized.
  1344.  *
  1345.  *----------------------------------------------------------------------
  1346.  */
  1347. static void
  1348. InitPartitions(partPtr, length)
  1349.     register Partition *partPtr;/* Array of partitions to be initialized */
  1350.     int length;            /* Number of elements in array */
  1351. {
  1352.     register int i;
  1353.  
  1354.     for (i = 0; i < length; i++) {
  1355.     partPtr->resize = RESIZE_BOTH;
  1356.     partPtr->reqSize.nom = partPtr->reqSize.min =
  1357.         partPtr->size = DEF_MIN_LIMIT;
  1358.     partPtr->reqSize.max = DEF_MAX_LIMIT;
  1359.     partPtr->nomSize = 0;
  1360.     partPtr->pad = 0;
  1361.     partPtr->span = 0;
  1362.     partPtr++;
  1363.     }
  1364. }
  1365.  
  1366. /*
  1367.  *----------------------------------------------------------------------
  1368.  *
  1369.  * ExtendArray --
  1370.  *
  1371.  *    Resizes the partition array to a larger size.
  1372.  *
  1373.  * Results:
  1374.  *    A pointer to the newly extended array is returned.
  1375.  *
  1376.  *----------------------------------------------------------------------
  1377.  */
  1378. static Partition *
  1379. ExtendArray(partArr, oldSize, newSize)
  1380.     Partition *partArr;        /*  */
  1381.     int oldSize, newSize;
  1382. {
  1383.     Partition *newArr;
  1384.  
  1385.     newArr = (Partition *)ckalloc(newSize * sizeof(Partition));
  1386.     if (newArr != NULL) {
  1387.     if (oldSize > 0) {
  1388.         memcpy((char *)newArr, (char *)partArr,
  1389.         oldSize * sizeof(Partition));
  1390.     }
  1391.     InitPartitions(&(newArr[oldSize]), (int)(newSize - oldSize));
  1392.     }
  1393.     return (newArr);
  1394. }
  1395.  
  1396. /*
  1397.  *----------------------------------------------------------------------
  1398.  *
  1399.  * AssertColumn --
  1400.  *
  1401.  *    Checks the size of the column partitions and extends the
  1402.  *    size if a larger array is needed.
  1403.  *
  1404.  * Results:
  1405.  *    Returns 1 if the column exists.  Otherwise 0 is returned and
  1406.  *    interp->result contains an error message.
  1407.  *
  1408.  * Side effects:
  1409.  *    The size of the column partition array may be extended and
  1410.  *    initialized.
  1411.  *
  1412.  *----------------------------------------------------------------------
  1413.  */
  1414. static int
  1415. AssertColumn(tablePtr, column)
  1416.     Table *tablePtr;
  1417.     int column;
  1418. {
  1419.     if (column >= tablePtr->colSize) {
  1420.     int newSize;
  1421.     Partition *newArr;
  1422.  
  1423.     if (column >= USHRT_MAX) {
  1424.         Tcl_AppendResult(tablePtr->interp, "too many columns in \"",
  1425.         Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
  1426.         return 0;
  1427.     }
  1428.     newSize = 2 * tablePtr->colSize;
  1429.     while (newSize <= column) {
  1430.         newSize += newSize;
  1431.     }
  1432.     newArr = ExtendArray(tablePtr->colPtr, tablePtr->colSize, newSize);
  1433.     if (newArr == NULL) {
  1434.         Tcl_AppendResult(tablePtr->interp, "can't extend columns in table",
  1435.         " \"", Tk_PathName(tablePtr->tkwin), "\": ",
  1436.         Tcl_PosixError(tablePtr->interp));
  1437.         return 0;
  1438.     }
  1439.     if (tablePtr->colPtr != tablePtr->colSpace) {
  1440.         ckfree((char *)tablePtr->colPtr);
  1441.     }
  1442.     tablePtr->colPtr = newArr;
  1443.     tablePtr->colSize = newSize;
  1444.     }
  1445.     if (column >= tablePtr->numCols) {
  1446.     tablePtr->numCols = column + 1;
  1447.     }
  1448.     return 1;
  1449. }
  1450.  
  1451. /*
  1452.  *----------------------------------------------------------------------
  1453.  *
  1454.  * AssertRow --
  1455.  *
  1456.  *    Checks the size of the row partitions and extends the
  1457.  *    size if a larger array is needed.
  1458.  *
  1459.  * Results:
  1460.  *    Returns 1 if the row exists.  Otherwise 0 is returned
  1461.  *    and interp->result contains an error message.
  1462.  *
  1463.  * Side effects:
  1464.  *    The size of the row partition array may be extended and
  1465.  *    initialized.
  1466.  *
  1467.  *----------------------------------------------------------------------
  1468.  */
  1469. static int
  1470. AssertRow(tablePtr, row)
  1471.     Table *tablePtr;
  1472.     int row;
  1473. {
  1474.     if (row >= tablePtr->rowSize) {
  1475.     register int newSize;
  1476.     Partition *newArr;
  1477.  
  1478.     if (row >= USHRT_MAX) {
  1479.         Tcl_AppendResult(tablePtr->interp, "too many rows in \"",
  1480.         Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
  1481.         return 0;
  1482.     }
  1483.     newSize = 2 * tablePtr->rowSize;
  1484.     while (newSize <= row) {
  1485.         newSize += newSize;
  1486.     }
  1487.     newArr = ExtendArray(tablePtr->rowPtr, tablePtr->rowSize, newSize);
  1488.     if (newArr == NULL) {
  1489.         Tcl_AppendResult(tablePtr->interp, "can't extend rows in table \"",
  1490.         Tk_PathName(tablePtr->tkwin), "\": ",
  1491.         Tcl_PosixError(tablePtr->interp));
  1492.         return 0;
  1493.     }
  1494.     if (tablePtr->rowPtr != tablePtr->rowSpace) {
  1495.         ckfree((char *)tablePtr->rowPtr);
  1496.     }
  1497.     tablePtr->rowPtr = newArr;
  1498.     tablePtr->rowSize = newSize;
  1499.     }
  1500.     if (row >= tablePtr->numRows) {
  1501.     tablePtr->numRows = row + 1;
  1502.     }
  1503.     return 1;
  1504. }
  1505.  
  1506. /*
  1507.  *----------------------------------------------------------------------
  1508.  *
  1509.  * ParseIndex --
  1510.  *
  1511.  *    Parse the entry index string and return the row and column
  1512.  *    numbers in their respective parameters.  The format of a
  1513.  *    table entry index is <row>,<column> where <row> is the row
  1514.  *    number and <column> is the column number.  Rows and columns
  1515.  *    are numbered starting from zero.
  1516.  *
  1517.  * Results:
  1518.  *    Returns a standard Tcl result.  If TCL_OK is returned, the
  1519.  *    row and column numbers are returned via rowPtr and columnPtr
  1520.  *    respectively.
  1521.  *
  1522.  *----------------------------------------------------------------------
  1523.  */
  1524. static int
  1525. ParseIndex(interp, indexStr, rowPtr, columnPtr)
  1526.     Tcl_Interp *interp;
  1527.     char *indexStr;
  1528.     int *rowPtr;
  1529.     int *columnPtr;
  1530. {
  1531.     char *columnStr, *rowStr;
  1532.     long row, column;
  1533.  
  1534.     rowStr = indexStr;
  1535.     columnStr = strchr(indexStr, ',');
  1536.     if (columnStr == NULL) {
  1537.     Tcl_AppendResult(interp, "invalid index \"", indexStr,
  1538.         "\": should be \"row,column\"", (char *)NULL);
  1539.     return TCL_ERROR;
  1540.  
  1541.     }
  1542.     *columnStr++ = '\0';
  1543.     if ((Tcl_ExprLong(interp, rowStr, &row) != TCL_OK) ||
  1544.     (Tcl_ExprLong(interp, columnStr, &column) != TCL_OK)) {
  1545.     return TCL_ERROR;
  1546.     }
  1547.     if ((row < 0) || (row > USHRT_MAX)) {
  1548.     Tcl_AppendResult(interp, "row index \"", rowStr,
  1549.         "\" is out of range", (char *)NULL);
  1550.     return TCL_ERROR;
  1551.     }
  1552.     if ((column < 0) || (column > USHRT_MAX)) {
  1553.     Tcl_AppendResult(interp, "column index \"", columnStr,
  1554.         "\" is out of range", (char *)NULL);
  1555.     return TCL_ERROR;
  1556.     }
  1557.     *rowPtr = (int)row;
  1558.     *columnPtr = (int)column;
  1559.     return TCL_OK;
  1560. }
  1561.  
  1562. /*
  1563.  *----------------------------------------------------------------------
  1564.  *
  1565.  * MakeSlaveKey --
  1566.  *
  1567.  *    Creates a one word key out of the two 16 bit row and column
  1568.  *    indices.
  1569.  *
  1570.  *----------------------------------------------------------------------
  1571.  */
  1572. static unsigned int
  1573. MakeSlaveKey(row, column)
  1574.     int row;
  1575.     int column;
  1576. {
  1577. #ifndef cray
  1578.     SlaveKey key;
  1579.  
  1580.     key.position.row = (unsigned short)row;
  1581.     key.position.column = (unsigned short)column;
  1582.     return (key.index);
  1583. #else
  1584.     unsigned int index;
  1585.  
  1586.     index = (row & 0xffffffff);
  1587.     index |= ((column & 0xffffffff) << 32);
  1588.     return (index);
  1589. #endif /*cray*/
  1590. }
  1591.  
  1592. /*
  1593.  *----------------------------------------------------------------------
  1594.  *
  1595.  * LinkRowEntry --
  1596.  *
  1597.  *    Links new list entry into list of row-sorted cubicles.
  1598.  *    It's important to maintain this list, because the size of
  1599.  *    the row parititions is determined in order of this list.
  1600.  *
  1601.  * Results:
  1602.  *    None.
  1603.  *
  1604.  * Side Effects:
  1605.  *    Entry is linked into the list of row-sorted cubicles.
  1606.  *
  1607.  *----------------------------------------------------------------------
  1608.  */
  1609. static void
  1610. LinkRowEntry(newPtr)
  1611.     Cubicle *newPtr;
  1612. {
  1613.     register int delta;
  1614.     Cubicle *cubiPtr;
  1615.     Table *tablePtr;
  1616.     register Blt_ListEntry *entryPtr;
  1617.  
  1618.     tablePtr = newPtr->tablePtr;
  1619.     for (entryPtr = Blt_FirstListEntry(&(tablePtr->rowSorted));
  1620.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1621.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  1622.     delta = newPtr->rowSpan - cubiPtr->rowSpan;
  1623.     if (delta < 0) {
  1624.         break;
  1625.     } else if (delta == 0) {
  1626.         delta = newPtr->rowIndex - cubiPtr->rowIndex;
  1627.         if (delta > 0) {
  1628.         break;
  1629.         } else if (delta == 0) {
  1630.         delta = newPtr->colIndex - cubiPtr->colIndex;
  1631.         if (delta > 0) {
  1632.             break;
  1633.         }
  1634.         }
  1635.     }
  1636.     }
  1637.     if (entryPtr == NULL) {
  1638.     Blt_LinkListAfter(&(tablePtr->rowSorted), newPtr->rowEntryPtr,
  1639.         entryPtr);
  1640.     } else {
  1641.     Blt_LinkListBefore(&(tablePtr->rowSorted), newPtr->rowEntryPtr,
  1642.         entryPtr);
  1643.     }
  1644. }
  1645.  
  1646. /*
  1647.  *----------------------------------------------------------------------
  1648.  *
  1649.  * LinkColumnEntry --
  1650.  *
  1651.  *    Links new list entry into list of column-sorted cubicles.
  1652.  *    It's important to maintain this list, because the size of
  1653.  *    the column parititions is determined in order of this list.
  1654.  *
  1655.  * Results:
  1656.  *    None.
  1657.  *
  1658.  * Side Effects:
  1659.  *    Entry is linked into the list of column-sorted cubicles.
  1660.  *
  1661.  *----------------------------------------------------------------------
  1662.  */
  1663. static void
  1664. LinkColumnEntry(newPtr)
  1665.     Cubicle *newPtr;
  1666. {
  1667.     register int delta;
  1668.     Cubicle *cubiPtr;
  1669.     Table *tablePtr;
  1670.     register Blt_ListEntry *entryPtr;
  1671.  
  1672.     tablePtr = newPtr->tablePtr;
  1673.     for (entryPtr = Blt_FirstListEntry(&(tablePtr->colSorted));
  1674.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1675.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  1676.     delta = newPtr->colSpan - cubiPtr->colSpan;
  1677.     if (delta < 0) {
  1678.         break;
  1679.     } else if (delta == 0) {
  1680.         delta = newPtr->colIndex - cubiPtr->colIndex;
  1681.         if (delta > 0) {
  1682.         break;
  1683.         } else if (delta == 0) {
  1684.         delta = newPtr->rowIndex - cubiPtr->rowIndex;
  1685.         if (delta > 0) {
  1686.             break;
  1687.         }
  1688.         }
  1689.     }
  1690.     }
  1691.     if (entryPtr == NULL) {
  1692.     Blt_LinkListAfter(&(tablePtr->colSorted), newPtr->colEntryPtr,
  1693.         entryPtr);
  1694.     } else {
  1695.     Blt_LinkListBefore(&(tablePtr->colSorted), newPtr->colEntryPtr,
  1696.         entryPtr);
  1697.     }
  1698. }
  1699.  
  1700. /*
  1701.  *----------------------------------------------------------------------
  1702.  *
  1703.  * AddWindowToTable --
  1704.  *
  1705.  *    Adds the given window as a slave window into the table at
  1706.  *    a given row and column position.  The window may already
  1707.  *    exist as a slave window in the table. If tkwin is a slave
  1708.  *    of another table, it's an error.
  1709.  *
  1710.  *    The new window is inserted into both the slave window hash
  1711.  *    table (this is used to locate the information associated with
  1712.  *    the slave window without searching each table) and cubicle
  1713.  *    lists which are sorted in order of column and row spans.
  1714.  *
  1715.  * Results:
  1716.  *    Returns a standard Tcl result.  If an error occurred, TCL_ERROR
  1717.  *    is returned and an error message is left in interp->result.
  1718.  *
  1719.  * Side Effects:
  1720.  *    The table is re-computed and arranged at the next idle point.
  1721.  *
  1722.  *----------------------------------------------------------------------
  1723.  */
  1724. static int
  1725. AddWindowToTable(tablePtr, tkwin, row, column, argc, argv)
  1726.     Table *tablePtr;
  1727.     Tk_Window tkwin;
  1728.     int row, column;
  1729.     int argc;
  1730.     char **argv;
  1731. {
  1732.     register Cubicle *cubiPtr;
  1733.     Blt_ListEntry *entryPtr;
  1734.     int result = TCL_OK;
  1735.     unsigned int key;
  1736.  
  1737.     cubiPtr = FindCubicle(tablePtr->interp, tkwin, 0);
  1738.     if (cubiPtr != NULL) {
  1739.     /*
  1740.      * Make sure the window is currently being managed by this table.
  1741.      */
  1742.     if (cubiPtr->tablePtr != tablePtr) {
  1743.         Tcl_AppendResult(tablePtr->interp, "\"", Tk_PathName(tkwin),
  1744.         "\" is already managed by \"", Tk_PathName(cubiPtr->tkwin),
  1745.         "\"", (char *)NULL);
  1746.         return TCL_ERROR;
  1747.     }
  1748.     /*
  1749.      * Remove the cubicle from both row and column lists.  It will
  1750.      * be re-inserted into the table at the new position
  1751.      */
  1752.     Blt_DeleteListEntry(&(tablePtr->rowSorted), cubiPtr->rowEntryPtr);
  1753.     Blt_DeleteListEntry(&(tablePtr->colSorted), cubiPtr->colEntryPtr);
  1754.     } else {
  1755.     cubiPtr = CreateCubicle(tablePtr, tkwin);
  1756.     if (cubiPtr == NULL) {
  1757.         return TCL_ERROR;
  1758.     }
  1759.     }
  1760.     /*
  1761.      * If there's already a slave window at this position in the
  1762.      * table, unmap it and remove the cubicle.
  1763.      */
  1764.     key = MakeSlaveKey(row, column);
  1765.     entryPtr = Blt_FindListEntry(tablePtr->listPtr, (char *)key);
  1766.     if (entryPtr != NULL) {
  1767.     Cubicle *oldPtr;
  1768.  
  1769.     oldPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  1770.     if (Tk_IsMapped(oldPtr->tkwin)) {
  1771.         Tk_UnmapWindow(oldPtr->tkwin);
  1772.     }
  1773.     DestroyCubicle(oldPtr);
  1774.     }
  1775.     cubiPtr->colIndex = column;
  1776.     cubiPtr->rowIndex = row;
  1777.     if (!AssertRow(tablePtr, cubiPtr->rowIndex) ||
  1778.     !AssertColumn(tablePtr, cubiPtr->colIndex)) {
  1779.     return TCL_ERROR;
  1780.     }
  1781.     if (argc > 0) {
  1782.     result = Tk_ConfigureWidget(tablePtr->interp, cubiPtr->tkwin,
  1783.         cubicleConfigSpecs, argc, argv, (char *)cubiPtr,
  1784.         TK_CONFIG_ARGV_ONLY);
  1785.     }
  1786.     if ((cubiPtr->colSpan < 1) || (cubiPtr->rowSpan < 1)) {
  1787.     Tcl_AppendResult(tablePtr->interp, "invalid spans specified for \"",
  1788.         Tk_PathName(tkwin), "\"", (char *)NULL);
  1789.     DestroyCubicle(cubiPtr);
  1790.     return TCL_ERROR;
  1791.     }
  1792.     /*
  1793.      * Insert the cubicle into both the row and column layout lists
  1794.      */
  1795.     cubiPtr->rowEntryPtr = Blt_CreateListEntry((char *)key);
  1796.     Blt_SetListValue(cubiPtr->rowEntryPtr, cubiPtr);
  1797.     LinkRowEntry(cubiPtr);
  1798.  
  1799.     cubiPtr->colEntryPtr = Blt_CreateListEntry((char *)key);
  1800.     Blt_SetListValue(cubiPtr->colEntryPtr, cubiPtr);
  1801.     LinkColumnEntry(cubiPtr);
  1802.  
  1803.     if (!AssertColumn(tablePtr, cubiPtr->colIndex + cubiPtr->colSpan - 1) ||
  1804.     !AssertRow(tablePtr, cubiPtr->rowIndex + cubiPtr->rowSpan - 1)) {
  1805.     return TCL_ERROR;
  1806.     }
  1807.     return (result);
  1808. }
  1809.  
  1810. /*
  1811.  *----------------------------------------------------------------------
  1812.  *
  1813.  * ManageWindows --
  1814.  *
  1815.  *    Processes an argv/argc list of table entries to add and
  1816.  *    configure new slave windows into the table.  A table entry
  1817.  *    consists of the window path name, table index, and optional
  1818.  *    configuration options.  The first argument in the argv list
  1819.  *    is the name of the table.  If no table exists for the given
  1820.  *    window, a new one is created.
  1821.  *
  1822.  * Results:
  1823.  *    Returns a standard Tcl result.  If an error occurred, TCL_ERROR
  1824.  *    is returned and an error message is left in interp->result.
  1825.  *
  1826.  * Side Effects:
  1827.  *    Memory is allocated, a new master table is possibly created, etc.
  1828.  *    The table is re-computed and arranged at the next idle point.
  1829.  *
  1830.  *----------------------------------------------------------------------
  1831.  */
  1832. static int
  1833. ManageWindows(tablePtr, interp, argc, argv)
  1834.     Table *tablePtr;        /* Table to manage new slave windows */
  1835.     Tcl_Interp *interp;        /* Interpreter to report errors back to */
  1836.     int argc;            /*  */
  1837.     char **argv;        /* List of slave windows, indices, and
  1838.                  * options */
  1839. {
  1840.     char *savePtr;
  1841.     int row, column;
  1842.     register int i, count;
  1843.     Tk_Window tkwin;
  1844.  
  1845.     for (i = 0; i < argc; /*empty*/ ) {
  1846.     tkwin = Tk_NameToWindow(interp, argv[i], tablePtr->tkwin);
  1847.     if (tkwin == NULL) {
  1848.         return TCL_ERROR;
  1849.     }
  1850.     if ((i + 1) == argc) {
  1851.         Tcl_AppendResult(interp, "missing index argument for \"", argv[i],
  1852.         "\"", (char *)NULL);
  1853.         return TCL_ERROR;
  1854.     }
  1855.     if (ParseIndex(interp, argv[i + 1], &row, &column) != TCL_OK) {
  1856.         return TCL_ERROR;    /* Invalid row,column index */
  1857.     }
  1858.     /*
  1859.      * Find the end this entry's option-value pairs, first
  1860.      * skipping over the slave window's pathname and table index
  1861.      * arguments.
  1862.      */
  1863.     i += 2;
  1864.     for (count = i; count < argc; count += 2) {
  1865.         if (argv[count][0] != '-') {
  1866.         break;
  1867.         }
  1868.     }
  1869.     savePtr = argv[count];
  1870.     argv[count] = NULL;
  1871.     if (AddWindowToTable(tablePtr, tkwin, row, column, count - i,
  1872.         argv + i) != TCL_OK) {
  1873.         return TCL_ERROR;
  1874.     }
  1875.     argv[count] = savePtr;
  1876.     i = count;
  1877.     }
  1878.     /* If all went well, arrange for the table layout to be performed. */
  1879.     tablePtr->flags |= REQUEST_LAYOUT;
  1880.     if (!(tablePtr->flags & ARRANGE_PENDING)) {
  1881.     tablePtr->flags |= ARRANGE_PENDING;
  1882.     Tk_DoWhenIdle(ArrangeTable, (ClientData)tablePtr);
  1883.     }
  1884.     interp->result = Tk_PathName(tablePtr->tkwin);
  1885.     return TCL_OK;
  1886. }
  1887.  
  1888. /*
  1889.  *----------------------------------------------------------------------
  1890.  *
  1891.  * SlaveNames --
  1892.  *
  1893.  *    Returns a list of all the pathnames of the slaves window
  1894.  *    managed by a table geometry manager that match the specified
  1895.  *    critereon.  The table is given
  1896.  *    by the path name of a master window associated with the table.
  1897.  *
  1898.  *      options:
  1899.  *      -match nnn    exclude windows not matching pattern
  1900.  *      -exclude nnn    exclude windows matching pattern
  1901.  *       -rows    n    exclude windows not in row n
  1902.  *      -column  n    exclude windows not in column n
  1903.  *
  1904.  * Results:
  1905.  *    Returns a standard Tcl result.  If no error occurred, TCL_OK
  1906.  *    is returned and a list of slave window path names is left in
  1907.  *    interp->result.
  1908.  *
  1909.  *----------------------------------------------------------------------
  1910.  */
  1911. /*ARGSUSED*/
  1912. static int
  1913. SlaveNames(tablePtr, interp, argc, argv)
  1914.     Table *tablePtr;
  1915.     Tcl_Interp *interp;        /* Interpreter to return list of names to */
  1916.     int argc;            /* Number of arguments */
  1917.     char **argv;        /* Contains 1-6 arguments: pathname of master
  1918.                  * window associated with the table, a search
  1919.                  * pattern, and a row,col pattern  */
  1920. {
  1921.     Blt_ListEntry *entryPtr;
  1922.     Cubicle *cubiPtr;
  1923. #   define NOPOS  -9999        /* kludge! must be an invalid row/col */
  1924.     long int rowmatch = NOPOS;    /* must match this row */
  1925.     long int colmatch = NOPOS;    /* must match this column */
  1926.     char *pattern = NULL;    /* pattern to match */
  1927.     char *exclude = NULL;    /* pattern to exclude */
  1928.     int length;
  1929.     int i;
  1930.     int result = TCL_OK;
  1931.  
  1932.     /* process arguments */
  1933.  
  1934.     if (argc == 2) {
  1935.     pattern = argv[1];    /* old style pattern */
  1936.     } else {
  1937.     for (i=1; i<argc-1 && result == TCL_OK; i+=2) {
  1938.         length = strlen(argv[i]);
  1939.         if (strncmp(argv[i], "-match", length) == 0) {
  1940.         pattern = argv[i+1];
  1941.         } else if (strncmp(argv[i], "-exclude", length) == 0) {
  1942.         exclude = argv[i+1];
  1943.         } else if (strncmp(argv[i], "-row", length) == 0) {
  1944.         result = Tcl_ExprLong(interp, argv[i+1], &rowmatch);
  1945.         } else if (strncmp(argv[i], "-column", length) == 0) {
  1946.         result = Tcl_ExprLong(interp, argv[i+1], &colmatch);
  1947.         } else {
  1948.         Tcl_AppendResult(interp, argv[i], " is invalid. Try: ",
  1949.             "-match, -exclude -row or -column", (char *)NULL);
  1950.         result = TCL_ERROR;
  1951.         }
  1952.     }
  1953.     if (result != TCL_OK) {
  1954.         return(result);
  1955.     }
  1956.     }
  1957.  
  1958.     for (entryPtr = Blt_FirstListEntry(tablePtr->listPtr);
  1959.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1960.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  1961.     if (pattern && !Tcl_StringMatch(Tk_PathName(cubiPtr->tkwin), pattern)) {
  1962.         continue;
  1963.     }
  1964.     if (exclude && Tcl_StringMatch(Tk_PathName(cubiPtr->tkwin), exclude)) {
  1965.         continue;
  1966.     }
  1967.     if (rowmatch!=NOPOS && (cubiPtr->rowIndex>rowmatch ||
  1968.         cubiPtr->rowIndex+cubiPtr->rowSpan<=rowmatch)) {
  1969.         continue;
  1970.     }
  1971.     if (colmatch!=NOPOS && (cubiPtr->colIndex>colmatch ||
  1972.         cubiPtr->colIndex+cubiPtr->colSpan<=colmatch)) {
  1973.         continue;
  1974.     }
  1975.     Tcl_AppendElement(interp, Tk_PathName(cubiPtr->tkwin));
  1976.     }
  1977.     return TCL_OK;
  1978. }
  1979.  
  1980. /*
  1981.  *----------------------------------------------------------------------
  1982.  *
  1983.  * MasterNames --
  1984.  *
  1985.  *    Returns a list of all the pathnames of the master windows
  1986.  *    managed by a table geometry manager matching a given pattern.
  1987.  *    If no pattern is present (argc == 0), all pathnames are returned.
  1988.  *
  1989.  * Results:
  1990.  *    Returns a standard Tcl result.  If no error occurred, TCL_OK
  1991.  *    is returned and a list of slave window path names is left in
  1992.  *    interp->result.
  1993.  *
  1994.  *----------------------------------------------------------------------
  1995.  */
  1996. /*ARGSUSED*/
  1997. static int
  1998. MasterNames(clientData, interp, argc, argv)
  1999.     ClientData clientData;    /* Main window of the interpreter: Used to
  2000.                  * search for windows in the hierarchy */
  2001.     Tcl_Interp *interp;        /* Interpreter to return list of names to */
  2002.     int argc;
  2003.     char **argv;        /* Contains 0-1 arguments: search pattern */
  2004. {
  2005.     Tk_Window searchWin = (Tk_Window)clientData;
  2006.     Tcl_HashEntry *entryPtr;
  2007.     Tcl_HashSearch cursor;
  2008.     register Table *tablePtr;
  2009.  
  2010.     for (entryPtr = Tcl_FirstHashEntry(&masterWindows, &cursor);
  2011.     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) {
  2012.     tablePtr = (Table *)Tcl_GetHashValue(entryPtr);
  2013.     if ((tablePtr->searchWin == searchWin) && ((argc != 1) ||
  2014.         (Tcl_StringMatch(Tk_PathName(tablePtr->tkwin), argv[0])))) {
  2015.         Tcl_AppendElement(interp, Tk_PathName(tablePtr->tkwin));
  2016.     }
  2017.     }
  2018.     return TCL_OK;
  2019. }
  2020.  
  2021. /*
  2022.  *----------------------------------------------------------------------
  2023.  *
  2024.  * PartitionInfo --
  2025.  *
  2026.  *
  2027.  *    Returns a list of row and column partition information.
  2028.  *    The information consists of the minimum size, current size,
  2029.  *    maximum size, and resize flag option.  The format should
  2030.  *    be suitable to pass back to the "configure" option.
  2031.  *
  2032.  * Results:
  2033.  *    Returns a standard Tcl result.
  2034.  *
  2035.  *----------------------------------------------------------------------
  2036.  */
  2037. /* ARGSUSED */
  2038. static int
  2039. PartitionInfo(tablePtr, interp, type, argc, argv)
  2040.     Table *tablePtr;
  2041.     Tcl_Interp *interp;
  2042.     PartitionTypes type;
  2043.     int argc;
  2044.     char **argv;
  2045. {
  2046.     long index;
  2047.     Partition *partPtr;
  2048.     char **indexArr;
  2049.     char buf[BUFSIZ];
  2050.     char string[200];
  2051.     char *format;
  2052.     int numPartitions, maxIndex;
  2053.     int queryAll;
  2054.     int result = TCL_ERROR;
  2055.     register int i;
  2056.  
  2057.     if (Tcl_SplitList(interp, argv[0], &numPartitions, &indexArr) != TCL_OK) {
  2058.     return TCL_ERROR;    /* Can't split list */
  2059.     }
  2060.     maxIndex = 0;        /* Inhibit compiler warnings */
  2061.     partPtr = NULL;
  2062.     format = NULL;
  2063.     if ((numPartitions == 1) &&
  2064.     (indexArr[0][0] == 'a') && (strcmp(indexArr[0], "all") == 0)) {
  2065.     numPartitions = NumEntries(tablePtr, type);
  2066.     queryAll = TRUE;
  2067.     } else {
  2068.     maxIndex = NumEntries(tablePtr, type);
  2069.     queryAll = FALSE;
  2070.     }
  2071.     for (i = 0; i < numPartitions; i++) {
  2072.     if (queryAll) {
  2073.         index = i;
  2074.     } else {
  2075.         if (Tcl_ExprLong(interp, indexArr[i], &index) != TCL_OK) {
  2076.         goto error;
  2077.         }
  2078.         if ((index < 0) || (index >= maxIndex)) {
  2079.         Tcl_AppendResult(interp, "index \"", indexArr[i],
  2080.             "\" is out of range", (char *)NULL);
  2081.         goto error;
  2082.         }
  2083.     }
  2084.     if (type == ROW_PARTITION_TYPE) {
  2085.         partPtr = tablePtr->rowPtr + index;
  2086.         format = "%d -resize %s -height {%s} -pady %d";
  2087.     } else if (type == COLUMN_PARTITION_TYPE) {
  2088.         partPtr = tablePtr->colPtr + index;
  2089.         format = "%d -resize %s -width {%s} -padx %d";
  2090.     }
  2091.     LimitsToString(partPtr->reqSize.min, partPtr->reqSize.max,
  2092.         partPtr->reqSize.nom, string);
  2093.     sprintf(buf, format, index, resizeStrings[(int)partPtr->resize],
  2094.         string, partPtr->pad);
  2095.     Tcl_AppendElement(tablePtr->interp, buf);
  2096.     }
  2097.     result = TCL_OK;
  2098.   error:
  2099.     ckfree((char *)indexArr);
  2100.     return result;
  2101. }
  2102.  
  2103. /*
  2104.  *----------------------------------------------------------------------
  2105.  *
  2106.  * LocationInfo --
  2107.  *
  2108.  *
  2109.  *    Returns the row or column index given a pixel coordinate
  2110.  *
  2111.  * Results:
  2112.  *    Returns a standard Tcl result.
  2113.  *
  2114.  *----------------------------------------------------------------------
  2115.  */
  2116. /* ARGSUSED */
  2117. static int
  2118. LocationInfo(tablePtr, interp, type, indexStr)
  2119.     Table *tablePtr;
  2120.     Tcl_Interp *interp;
  2121.     PartitionTypes type;
  2122.     char *indexStr;
  2123. {
  2124.     long sum = 0;
  2125.     long index;
  2126.     Partition *partPtr;
  2127.     char string[20];
  2128.     int numPartitions;
  2129.     register int i;
  2130.  
  2131.     if (Tcl_ExprLong(interp, indexStr, &index) != TCL_OK) {
  2132.     return TCL_ERROR;
  2133.     }
  2134.     if (index < 0) {
  2135.     sprintf(string, "%d",-1);
  2136.     Tcl_AppendElement(tablePtr->interp, string);
  2137.     return TCL_OK;
  2138.     }
  2139.     numPartitions = NumEntries(tablePtr, type);
  2140.     partPtr = ((type == ROW_PARTITION_TYPE)
  2141.     ? tablePtr->rowPtr : tablePtr->colPtr);
  2142.     for (i = 0; i < numPartitions; i++) {
  2143.     sum += partPtr[i].size;
  2144.     if (index < sum) break;
  2145.     }
  2146.     sprintf(string, "%d",i);
  2147.     Tcl_AppendElement(tablePtr->interp, string);
  2148.     return TCL_OK;
  2149. }
  2150.  
  2151.  
  2152. /*
  2153.  *----------------------------------------------------------------------
  2154.  *
  2155.  * PartitionSizes --
  2156.  *
  2157.  *
  2158.  *    Returns the sizes of the named partitions (rows or columns)
  2159.  *
  2160.  * Results:
  2161.  *    Returns a standard Tcl result.
  2162.  *
  2163.  *----------------------------------------------------------------------
  2164.  */
  2165. /* ARGSUSED */
  2166. static int
  2167. PartitionSizes(tablePtr, interp, type, indexStr)
  2168.     Table *tablePtr;
  2169.     Tcl_Interp *interp;
  2170.     PartitionTypes type;
  2171.     char *indexStr;
  2172. {
  2173.     long index;
  2174.     Partition *partPtr;
  2175.     char **indexArr;
  2176.     char string[200];
  2177.     int numPartitions, maxIndex;
  2178.     int queryAll;
  2179.     int result = TCL_ERROR;
  2180.     register int i;
  2181.  
  2182.     if (Tcl_SplitList(interp, indexStr, &numPartitions, &indexArr) != TCL_OK) {
  2183.     return TCL_ERROR;    /* Can't split list */
  2184.     }
  2185.     maxIndex = 0;        /* Suppress compiler warning */
  2186.     if ((numPartitions == 1) &&
  2187.     (indexArr[0][0] == 'a') && (strcmp(indexArr[0], "all") == 0)) {
  2188.     numPartitions = NumEntries(tablePtr, type);
  2189.     queryAll = TRUE;
  2190.     } else {
  2191.     maxIndex = NumEntries(tablePtr, type);
  2192.     queryAll = FALSE;
  2193.     }
  2194.     partPtr = ((type == ROW_PARTITION_TYPE)
  2195.     ? tablePtr->rowPtr : tablePtr->colPtr);
  2196.     for (i = 0; i < numPartitions; i++) {
  2197.     if (queryAll) {
  2198.         index = i;
  2199.     } else {
  2200.         if (Tcl_ExprLong(interp, indexArr[i], &index) != TCL_OK) {
  2201.         goto error;
  2202.         }
  2203.         if ((index < 0) || (index >= maxIndex)) {
  2204.         Tcl_AppendResult(interp, "index \"", indexArr[i],
  2205.             "\" is out of range", (char *)NULL);
  2206.         goto error;
  2207.         }
  2208.     }
  2209.     sprintf(string, "%d", partPtr[index].size);
  2210.     Tcl_AppendElement(tablePtr->interp, string);
  2211.     }
  2212.     result = TCL_OK;
  2213.   error:
  2214.     ckfree((char *)indexArr);
  2215.     return result;
  2216. }
  2217.  
  2218. /*
  2219.  *----------------------------------------------------------------------
  2220.  *
  2221.  * LayoutTable --
  2222.  *
  2223.  *    Forces layout of the table geometry manager.  This is useful
  2224.  *    to get the geometry manager to calculate the normal width and
  2225.  *    height of each row and column.  Otherwise, one needs to
  2226.  *    withdraw the master window, run "update", and then query to
  2227.  *    geometry manager.
  2228.  *
  2229.  * Results:
  2230.  *    Returns a standard Tcl result.  If no error occurred, TCL_OK
  2231.  *    is returned. Otherwise, TCL_ERROR and a error message is left in
  2232.  *    interp->result.
  2233.  *
  2234.  *----------------------------------------------------------------------
  2235.  */
  2236. static int
  2237. LayoutTable(tablePtr)
  2238.     Table *tablePtr;
  2239. {
  2240.     tablePtr->flags |= REQUEST_LAYOUT | ARRANGE_PENDING;
  2241.     ArrangeTable((ClientData)tablePtr);
  2242.     return TCL_OK;
  2243. }
  2244.  
  2245. /*
  2246.  *----------------------------------------------------------------------
  2247.  *
  2248.  * SlaveInfo --
  2249.  *
  2250.  *    Returns the name, position and options of a slave in the table.
  2251.  *
  2252.  * Results:
  2253.  *    Returns a standard Tcl result.  A list of the slave window
  2254.  *    attributes is left in interp->result.
  2255.  *
  2256.  *----------------------------------------------------------------------
  2257.  */
  2258. /*ARGSUSED*/
  2259. static int
  2260. SlaveInfo(clientData, interp, name)
  2261.     ClientData clientData;
  2262.     Tcl_Interp *interp;
  2263.     char *name;
  2264. {
  2265.     Tk_Window searchWin = (Tk_Window)clientData;
  2266.     Tk_Window tkwin;
  2267.     Cubicle *cubiPtr;
  2268.     char string[200];
  2269.  
  2270.     tkwin = Tk_NameToWindow(interp, name, searchWin);
  2271.     if (tkwin == NULL) {
  2272.     return TCL_ERROR;
  2273.     }
  2274.     cubiPtr = FindCubicle(interp, tkwin, TCL_LEAVE_ERR_MSG);
  2275.     if (cubiPtr == NULL) {
  2276.     return TCL_ERROR;
  2277.     }
  2278.     sprintf(string, " %s %d,%d", Tk_PathName(cubiPtr->tkwin),
  2279.     cubiPtr->rowIndex, cubiPtr->colIndex);
  2280.     Tcl_AppendResult(interp, string, (char *)NULL);
  2281.     sprintf(string, " -ipadx %d -ipady %d -padx %d -pady %d", cubiPtr->ipadX,
  2282.     cubiPtr->ipadY, cubiPtr->padX, cubiPtr->padY);
  2283.     Tcl_AppendResult(interp, string, (char *)NULL);
  2284.     sprintf(string, " -rowspan %d -columnspan %d", cubiPtr->rowSpan,
  2285.     cubiPtr->colSpan);
  2286.     Tcl_AppendResult(interp, string, (char *)NULL);
  2287.     Tcl_AppendResult(interp, " -anchor ", Tk_NameOfAnchor(cubiPtr->anchor),
  2288.     " -fill ", fillStrings[(int)cubiPtr->fill], (char *)NULL);
  2289.     LimitsToString(cubiPtr->reqWidth.min, cubiPtr->reqWidth.max,
  2290.     cubiPtr->reqWidth.nom, string);
  2291.     Tcl_AppendResult(interp, " -reqwidth {", string, "}", (char *)NULL);
  2292.     LimitsToString(cubiPtr->reqHeight.min, cubiPtr->reqHeight.max,
  2293.     cubiPtr->reqHeight.nom, string);
  2294.     Tcl_AppendResult(interp, " -reqheight {", string, "}", (char *)NULL);
  2295. #ifdef LATER
  2296.     sprintf(string,"%s",Tk_PathName(cubiPtr->tablePtr->tkwin));
  2297.     Tcl_AppendResult(interp, " -in ", string, (char *)NULL);
  2298. #endif
  2299.     return TCL_OK;
  2300. }
  2301.  
  2302. /*
  2303.  *----------------------------------------------------------------------
  2304.  *
  2305.  * ConfigurePartition --
  2306.  *
  2307.  *    This procedure is called to process an argv/argc list in order
  2308.  *    to configure a row or column in the table geometry manager.
  2309.  *
  2310.  * Results:
  2311.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  2312.  *    returned, then interp->result contains an error message.
  2313.  *
  2314.  * Side effects:
  2315.  *    Partition configuration options (bounds, resize flags, etc)
  2316.  *    get set.  New partitions may be created as necessary. The table
  2317.  *    is recalculated and arranged at the next idle point.
  2318.  *
  2319.  *----------------------------------------------------------------------
  2320.  */
  2321. static int
  2322. ConfigurePartition(tablePtr, interp, type, argc, argv)
  2323.     Table *tablePtr;
  2324.     Tcl_Interp *interp;
  2325.     PartitionTypes type;
  2326.     int argc;
  2327.     char **argv;
  2328. {
  2329.     int index;
  2330.     Partition *partPtr;
  2331.     Tk_ConfigSpec *configSpecsPtr;
  2332.     char **indexArr;
  2333.     int numPartitions;
  2334.     int configureAll;
  2335.     int result = TCL_ERROR;
  2336.     register int i;
  2337.  
  2338.     if (Tcl_SplitList(interp, argv[0], &numPartitions, &indexArr) != TCL_OK) {
  2339.     return TCL_ERROR;    /* Can't split list */
  2340.     }
  2341.     configSpecsPtr = partConfigSpecs[(int)type];
  2342.     configureAll = FALSE;
  2343.  
  2344.     partPtr = NULL;
  2345.     if ((numPartitions == 1) &&
  2346.     (indexArr[0][0] == 'a') && (strcmp(indexArr[0], "all") == 0)) {
  2347.     numPartitions = NUMENTRIES(tablePtr, type);
  2348.     configureAll = TRUE;
  2349.     }
  2350.     for (i = 0; i < numPartitions; i++) {
  2351.     if (configureAll) {
  2352.         index = i;
  2353.     } else {
  2354.         long int value;
  2355.  
  2356.         if (Tcl_ExprLong(interp, indexArr[i], &value) != TCL_OK) {
  2357.         goto error;
  2358.         }
  2359.         index = (int)value;
  2360.         if ((index < 0) || (index > USHRT_MAX)) {
  2361.         Tcl_AppendResult(interp, "index \"", indexArr[i],
  2362.             "\" is out of range", (char *)NULL);
  2363.         goto error;
  2364.         }
  2365.     }
  2366.     if (type == ROW_PARTITION_TYPE) {
  2367.         if (!AssertRow(tablePtr, index)) {
  2368.         goto error;
  2369.         }
  2370.         partPtr = tablePtr->rowPtr + index;
  2371.     } else if (type == COLUMN_PARTITION_TYPE) {
  2372.         if (!AssertColumn(tablePtr, index)) {
  2373.         goto error;
  2374.         }
  2375.         partPtr = tablePtr->colPtr + index;
  2376.     }
  2377.     if (argc == 1) {
  2378.         ckfree((char *)indexArr);
  2379.         return (Tk_ConfigureInfo(interp, tablePtr->tkwin, configSpecsPtr,
  2380.             (char *)partPtr, (char *)NULL, 0));
  2381.     } else if (argc == 2) {
  2382.         ckfree((char *)indexArr);
  2383.         return (Tk_ConfigureInfo(interp, tablePtr->tkwin, configSpecsPtr,
  2384.             (char *)partPtr, argv[1], 0));
  2385.     }
  2386.     if (Tk_ConfigureWidget(interp, tablePtr->tkwin, configSpecsPtr,
  2387.         argc - 1, argv + 1, (char *)partPtr,
  2388.         TK_CONFIG_ARGV_ONLY) != TCL_OK) {
  2389.         goto error;
  2390.     }
  2391.     }
  2392.     tablePtr->flags |= REQUEST_LAYOUT;
  2393.     if (!(tablePtr->flags & ARRANGE_PENDING)) {
  2394.     tablePtr->flags |= ARRANGE_PENDING;
  2395.     Tk_DoWhenIdle(ArrangeTable, (ClientData)tablePtr);
  2396.     }
  2397.     result = TCL_OK;
  2398.   error:
  2399.     ckfree((char *)indexArr);
  2400.     return result;
  2401. }
  2402.  
  2403. /*
  2404.  *----------------------------------------------------------------------
  2405.  *
  2406.  * ForgetWindow --
  2407.  *
  2408.  *    Processes an argv/argc list of slave window names and  purges
  2409.  *    their entries from their respective tables.  The windows are
  2410.  *    unmapped and the tables are rearranged at the next idle point.
  2411.  *    Note that all the named slave windows do not need to exist in
  2412.  *    the same table.
  2413.  *
  2414.  * Results:
  2415.  *    Returns a standard Tcl result.  If an error occurred, TCL_ERROR
  2416.  *    is returned and an error message is left in interp->result.
  2417.  *
  2418.  * Side Effects:
  2419.  *    Memory is deallocated (the cubicle is destroyed), etc.
  2420.  *    The affected tables are is re-computed and arranged at the next
  2421.  *    idle point.
  2422.  *
  2423.  *----------------------------------------------------------------------
  2424.  */
  2425.  
  2426. static int
  2427. ForgetWindow(clientData, interp, argc, argv)
  2428.     ClientData clientData;
  2429.     Tcl_Interp *interp;
  2430.     int argc;
  2431.     char **argv;
  2432. {
  2433.     Cubicle *cubiPtr;
  2434.     register int i;
  2435.     Tk_Window tkwin;
  2436.  
  2437.     for (i = 0; i < argc; i++) {
  2438.     tkwin = Tk_NameToWindow(interp, argv[i], (Tk_Window)clientData);
  2439.     if (tkwin == NULL) {
  2440.         return TCL_ERROR;
  2441.     }
  2442.     cubiPtr = FindCubicle(interp, tkwin, TCL_LEAVE_ERR_MSG);
  2443.     if (cubiPtr == NULL) {
  2444.         return TCL_ERROR;
  2445.     }
  2446.     if (Tk_IsMapped(cubiPtr->tkwin)) {
  2447.         Tk_UnmapWindow(cubiPtr->tkwin);
  2448.     }
  2449.     /*
  2450.      * Arrange for the call back here because not all the named
  2451.      * slave windows may belong to the same table.
  2452.      */
  2453.     cubiPtr->tablePtr->flags |= REQUEST_LAYOUT;
  2454.     if (!(cubiPtr->tablePtr->flags & ARRANGE_PENDING)) {
  2455.         cubiPtr->tablePtr->flags |= ARRANGE_PENDING;
  2456.         Tk_DoWhenIdle(ArrangeTable, (ClientData)cubiPtr->tablePtr);
  2457.     }
  2458.     DestroyCubicle(cubiPtr);
  2459.     }
  2460.     return TCL_OK;
  2461. }
  2462.  
  2463. /*
  2464.  * -----------------------------------------------------------------
  2465.  *
  2466.  * TranslateAnchor --
  2467.  *
  2468.  *     Translate the coordinates of a given bounding box based
  2469.  *    upon the anchor specified.  The anchor indicates where
  2470.  *    the given xy position is in relation to the bounding box.
  2471.  *
  2472.  *          nw --- n --- ne
  2473.  *          |            |     x,y ---+
  2474.  *          w   center   e      |     |
  2475.  *          |            |      +-----+
  2476.  *          sw --- s --- se
  2477.  *
  2478.  * Results:
  2479.  *    The translated coordinates of the bounding box are returned.
  2480.  *
  2481.  * -----------------------------------------------------------------
  2482.  */
  2483. static XPoint
  2484. TranslateAnchor(deltaX, deltaY, anchor)
  2485.     int deltaX, deltaY;        /* Difference between outer and inner regions
  2486.                  */
  2487.     Tk_Anchor anchor;        /* Direction of the anchor */
  2488. {
  2489.     XPoint newPt;
  2490.  
  2491.     newPt.x = newPt.y = 0;
  2492.     switch (anchor) {
  2493.     case TK_ANCHOR_NW:        /* Upper left corner */
  2494.     break;
  2495.     case TK_ANCHOR_W:        /* Left center */
  2496.     newPt.y = (deltaY / 2);
  2497.     break;
  2498.     case TK_ANCHOR_SW:        /* Lower left corner */
  2499.     newPt.y = deltaY;
  2500.     break;
  2501.     case TK_ANCHOR_N:        /* Top center */
  2502.     newPt.x = (deltaX / 2);
  2503.     break;
  2504.     case TK_ANCHOR_CENTER:    /* Centered */
  2505.     newPt.x = (deltaX / 2);
  2506.     newPt.y = (deltaY / 2);
  2507.     break;
  2508.     case TK_ANCHOR_S:        /* Bottom center */
  2509.     newPt.x = (deltaX / 2);
  2510.     newPt.y = deltaY;
  2511.     break;
  2512.     case TK_ANCHOR_NE:        /* Upper right corner */
  2513.     newPt.x = deltaX;
  2514.     break;
  2515.     case TK_ANCHOR_E:        /* Right center */
  2516.     newPt.x = deltaX;
  2517.     newPt.y = (deltaY / 2);
  2518.     break;
  2519.     case TK_ANCHOR_SE:        /* Lower right corner */
  2520.     newPt.x = deltaX;
  2521.     newPt.y = deltaY;
  2522.     break;
  2523.     }
  2524.     return (newPt);
  2525. }
  2526.  
  2527. /*
  2528.  *----------------------------------------------------------------------
  2529.  *
  2530.  * GetReqWidth --
  2531.  *
  2532.  *    Returns the width requested by the slave window starting in
  2533.  *    the given cubicle.  The requested space also includes any
  2534.  *    internal padding which has been designated for this window.
  2535.  *
  2536.  *    The requested width of the window is always bounded by
  2537.  *    the limits set in cubiPtr->reqWidth.
  2538.  *
  2539.  * Results:
  2540.  *    Returns the requested width of the slave window.
  2541.  *
  2542.  *----------------------------------------------------------------------
  2543.  */
  2544. static int
  2545. GetReqWidth(cubiPtr)
  2546.     Cubicle *cubiPtr;
  2547. {
  2548.     register int width;
  2549.  
  2550.     if (cubiPtr->reqWidth.nom > 0) {
  2551.     width = cubiPtr->reqWidth.nom;
  2552.     } else {
  2553.     width = Tk_ReqWidth(cubiPtr->tkwin) + (2 * cubiPtr->ipadX);
  2554.     }
  2555.     if (width < cubiPtr->reqWidth.min) {
  2556.     width = cubiPtr->reqWidth.min;
  2557.     } else if (width > cubiPtr->reqWidth.max) {
  2558.     width = cubiPtr->reqWidth.max;
  2559.     }
  2560.     return (width);
  2561. }
  2562.  
  2563. /*
  2564.  *----------------------------------------------------------------------
  2565.  *
  2566.  * GetReqHeight --
  2567.  *
  2568.  *    Returns the height requested by the slave window starting in
  2569.  *    the given cubicle.  The requested space also includes any
  2570.  *    internal padding which has been designated for this window.
  2571.  *
  2572.  *    The requested height of the window is always bounded by
  2573.  *    the limits set in cubiPtr->reqHeight.
  2574.  *
  2575.  * Results:
  2576.  *    Returns the requested height of the slave window.
  2577.  *
  2578.  *----------------------------------------------------------------------
  2579.  */
  2580. static int
  2581. GetReqHeight(cubiPtr)
  2582.     Cubicle *cubiPtr;
  2583. {
  2584.     register int height;
  2585.  
  2586.     if (cubiPtr->reqHeight.nom > 0) {
  2587.     height = cubiPtr->reqHeight.nom;
  2588.     } else {
  2589.     height = Tk_ReqHeight(cubiPtr->tkwin) + (2 * cubiPtr->ipadY);
  2590.     }
  2591.     if (height < cubiPtr->reqHeight.min) {
  2592.     height = cubiPtr->reqHeight.min;
  2593.     } else if (height > cubiPtr->reqHeight.max) {
  2594.     height = cubiPtr->reqHeight.max;
  2595.     }
  2596.     return (height);
  2597. }
  2598.  
  2599. /*
  2600.  *----------------------------------------------------------------------
  2601.  *
  2602.  * GetSpan --
  2603.  *
  2604.  *    Calculates the distance of the given span of partitions.
  2605.  *
  2606.  * Results:
  2607.  *    Returns the space currently used in the span of partitions.
  2608.  *
  2609.  *----------------------------------------------------------------------
  2610.  */
  2611. static int
  2612. GetSpan(partArr, length, withPad)
  2613.     Partition *partArr;        /* Array of partitions */
  2614.     int length;            /* Number of partitions spanned */
  2615.     int withPad;        /* If non-zero, include the extra padding at
  2616.                  * the end partitions of the span in the space
  2617.                  * used */
  2618. {
  2619.     register Partition *partPtr;
  2620.     Partition *startPtr, *endPtr;
  2621.     register int spaceUsed;
  2622.  
  2623.     startPtr = partArr;
  2624.     endPtr = partArr + (length - 1);
  2625.  
  2626.     spaceUsed = 0;
  2627.     for (partPtr = startPtr; partPtr <= endPtr; partPtr++) {
  2628.     spaceUsed += partPtr->size;
  2629.     }
  2630.     if (!withPad) {
  2631.     spaceUsed -= (startPtr->pad + endPtr->pad);
  2632.     }
  2633.     return (spaceUsed);
  2634. }
  2635.  
  2636. /*
  2637.  *----------------------------------------------------------------------
  2638.  *
  2639.  * GrowSpan --
  2640.  *
  2641.  *    Expand the span by the amount of the extra space needed.
  2642.  *      This procedure is used in LayoutPartitions to grow the
  2643.  *    partitions to their minimum nominal size, starting from
  2644.  *    a zero width and height space.
  2645.  *
  2646.  *    This looks more complicated than it really is.  The idea is
  2647.  *    to make the size of the partitions correspond to the smallest
  2648.  *    cubicle spans.  For example, if window A is in column 1 and
  2649.  *    window B spans both columns 0 and 1, any extra space needed
  2650.  *    to fit window B should come from column 0.
  2651.  *
  2652.  *    On the first pass we try to add space to partitions which have
  2653.  *    not been touched yet (i.e. have no nominal size).  Since the
  2654.  *    row and column lists are sorted in ascending order of the number
  2655.  *    of rows or columns spanned, the space is distributed amongst the
  2656.  *    smallest spans first.
  2657.  *
  2658.  *    The second pass handles the case of windows which have the same
  2659.  *    span.  For example, if A and B, which span the same number of
  2660.  *    partitions are the only windows to span column 1, column 1 would
  2661.  *    grow to contain the bigger of the two slices of space.
  2662.  *
  2663.  *    If there is still extra space after the first two passes, this
  2664.  *    means that there were no partitions of with no window spans or
  2665.  *    the same order span that could be expanded. The third pass
  2666.  *    will try to remedy this by parcelling out the left over space
  2667.  *    evenly among the rest of the partitions.
  2668.  *
  2669.  *    On each pass, we have to keep iterating over the span, evenly
  2670.  *    doling out slices of extra space, because we may hit partition
  2671.  *    limits as space is donated.  In addition, if there are left
  2672.  *    over pixels because of round-off, this will distribute them as
  2673.  *    evenly as possible.  For the worst case, it will take *length*
  2674.  *    passes to expand the span.
  2675.  *
  2676.  * Results:
  2677.  *    None.
  2678.  *
  2679.  * Side Effects:
  2680.  *     The partitions in the span may be expanded.
  2681.  *
  2682.  *----------------------------------------------------------------------
  2683.  */
  2684. static void
  2685. GrowSpan(array, length, extraSpace)
  2686.     Partition *array;        /* Array of (column/row) partitions  */
  2687.     int length;            /* Number of partitions in the span */
  2688.     int extraSpace;        /* The amount of extra space needed to
  2689.                  * grow the span. */
  2690. {
  2691.     register Partition *partPtr;
  2692.     Partition *startPtr, *endPtr;
  2693.     int availSpace, adjustSize;
  2694.     int numAvail;        /* Number of partitions with space available */
  2695.     int growAll = 0;        /* grow partitions that don't want to grow */
  2696.  
  2697.     startPtr = array;
  2698.     endPtr = array + length;
  2699.  
  2700.     /*
  2701.      *-------------------------------------------------------------------
  2702.      *
  2703.      * Pass 1: Add space first to partitions which were previously empty
  2704.      *
  2705.      *-------------------------------------------------------------------
  2706.      */
  2707.  
  2708.     /* Find out how many partitions have no size yet */
  2709.     numAvail = 0;
  2710.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2711.     if ((partPtr->nomSize == 0) && (partPtr->maxSize > partPtr->size)) {
  2712.         numAvail++;
  2713.     }
  2714.     }
  2715.     while ((numAvail > 0) && (extraSpace > 0)) {
  2716.     adjustSize = extraSpace / numAvail;
  2717.     if (adjustSize == 0) {
  2718.         adjustSize = 1;
  2719.     }
  2720.     for (partPtr = startPtr; (partPtr < endPtr) && (extraSpace > 0);
  2721.         partPtr++) {
  2722.         availSpace = partPtr->maxSize - partPtr->size;
  2723.         if ((partPtr->nomSize == 0) && (availSpace > 0)) {
  2724.         if (adjustSize < availSpace) {
  2725.             extraSpace -= adjustSize;
  2726.             partPtr->size += adjustSize;
  2727.         } else {
  2728.             extraSpace -= availSpace;
  2729.             partPtr->size += availSpace;
  2730.             numAvail--;
  2731.         }
  2732.         partPtr->span = length;
  2733.         }
  2734.     }
  2735.     }
  2736.  
  2737.     /*
  2738.      *-------------------------------------------------------------------
  2739.      *
  2740.      * Pass 2: Add space to partitions which have the same minimum span
  2741.      *
  2742.      *-------------------------------------------------------------------
  2743.      */
  2744.  
  2745.     numAvail = 0;
  2746.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2747.     if ((partPtr->span == length) && (partPtr->maxSize > partPtr->size)) {
  2748.         numAvail++;
  2749.     }
  2750.     }
  2751.     while ((numAvail > 0) && (extraSpace > 0)) {
  2752.     adjustSize = extraSpace / numAvail;
  2753.     if (adjustSize == 0) {
  2754.         adjustSize = 1;
  2755.     }
  2756.     for (partPtr = startPtr; (partPtr < endPtr) && (extraSpace > 0);
  2757.         partPtr++) {
  2758.         availSpace = partPtr->maxSize - partPtr->size;
  2759.         if ((partPtr->span == length) && (availSpace > 0)) {
  2760.         if (adjustSize < availSpace) {
  2761.             extraSpace -= adjustSize;
  2762.             partPtr->size += adjustSize;
  2763.         } else {
  2764.             extraSpace -= availSpace;
  2765.             partPtr->size += availSpace;
  2766.             numAvail--;
  2767.         }
  2768.         }
  2769.     }
  2770.     }
  2771.  
  2772.     /*
  2773.      *-------------------------------------------------------------------
  2774.      *
  2775.      * Pass 3: Try to expand all the windows with space still available
  2776.      *
  2777.      *-------------------------------------------------------------------
  2778.      */
  2779.  
  2780.     /*
  2781.      * Find out how many partitions still have space available
  2782.      * and are willing to grow
  2783.      */
  2784.  
  2785.     numAvail = 0;
  2786.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2787.     if (partPtr->maxSize > partPtr->size && 
  2788.         partPtr->resize != RESIZE_NONE &&
  2789.         partPtr->resize != RESIZE_SHRINK) {
  2790.         numAvail++;
  2791.     }
  2792.     partPtr->nomSize = partPtr->size;
  2793.     }
  2794.  
  2795.     /*
  2796.      * No partitions wanted to grow, so divide the space equally
  2797.      */
  2798.  
  2799.     if (numAvail == 0) {
  2800.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2801.         if (partPtr->maxSize > partPtr->size) {
  2802.         numAvail++;
  2803.         }
  2804.     }
  2805.     growAll = 1;
  2806.     }
  2807.     while ((numAvail > 0) && (extraSpace > 0)) {
  2808.     adjustSize = extraSpace / numAvail;
  2809.     if (adjustSize == 0) {
  2810.         adjustSize = 1;
  2811.     }
  2812.     for (partPtr = startPtr; (partPtr < endPtr) && (extraSpace > 0);
  2813.         partPtr++) {
  2814.         if (!growAll && (partPtr->resize == RESIZE_NONE ||
  2815.                 partPtr->resize == RESIZE_SHRINK)) {
  2816.         continue;
  2817.         }
  2818.         availSpace = partPtr->maxSize - partPtr->size;
  2819.         if (availSpace > 0) {
  2820.         if (adjustSize < availSpace) {
  2821.             extraSpace -= adjustSize;
  2822.             partPtr->nomSize = partPtr->size =
  2823.             (partPtr->size + adjustSize);
  2824.         } else {
  2825.             extraSpace -= availSpace;
  2826.             partPtr->nomSize = partPtr->size =
  2827.             (partPtr->size + availSpace);
  2828.             numAvail--;
  2829.         }
  2830.         }
  2831.     }
  2832.     }
  2833. }
  2834.  
  2835. /*
  2836.  *----------------------------------------------------------------------
  2837.  *
  2838.  * AdjustPartitions --
  2839.  *
  2840.  *    Adjust the span by the amount of the extra space needed.
  2841.  *    If the amount (extraSpace) is negative, shrink the span,
  2842.  *    otherwise expand it.  Size constraints on the partitions may
  2843.  *    prevent any or all of the spacing adjustments.
  2844.  *
  2845.  *    This is very much like the GrowSpan procedure, but in this
  2846.  *    case we are shrinking or expanding all the (row or column)
  2847.  *    partitions. It uses a two pass approach, first giving
  2848.  *    space to partitions which not are smaller/larger than their
  2849.  *    nominal sizes. This is because constraints on the partitions
  2850.  *    may cause resizing to be non-linear.
  2851.  *
  2852.  *    If there is still extra space, this means that all partitions
  2853.  *    are at least to their nominal sizes.  The second pass will
  2854.  *    try to add/remove the left over space evenly among all the
  2855.  *    partitions which still have space available.
  2856.  *
  2857.  * Results:
  2858.  *    None.
  2859.  *
  2860.  * Side Effects:
  2861.  *     The size of the partitions in the span may be increased
  2862.  *    or decreased.
  2863.  *
  2864.  *----------------------------------------------------------------------
  2865.  */
  2866. static void
  2867. AdjustPartitions(array, length, extraSpace)
  2868.     Partition *array;        /* Array of (column/row) partitions  */
  2869.     int length;            /* Number of partitions */
  2870.     int extraSpace;        /* The amount of extra space to grow or shrink
  2871.                  * the span. If negative, it represents the
  2872.                  * amount of space to remove */
  2873. {
  2874.     register Partition *partPtr;
  2875.     Partition *startPtr, *endPtr;
  2876.     int availSpace, adjustSize;
  2877.     int numAvail;
  2878.  
  2879.     startPtr = array;
  2880.     endPtr = array + length;
  2881.  
  2882.     /*
  2883.      *-------------------------------------------------------------------
  2884.      *
  2885.      * Pass 1: Adjust partition's with space beyond its nominal size.
  2886.      *
  2887.      *-------------------------------------------------------------------
  2888.      */
  2889.     numAvail = 0;
  2890.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2891.     if (extraSpace < 0) {
  2892.         availSpace = partPtr->size - partPtr->nomSize;
  2893.     } else {
  2894.         availSpace = partPtr->nomSize - partPtr->size;
  2895.     }
  2896.     if (availSpace > 0) {
  2897.         numAvail++;
  2898.     }
  2899.     }
  2900.     while ((numAvail > 0) && (extraSpace != 0)) {
  2901.     adjustSize = extraSpace / numAvail;
  2902.     if (adjustSize == 0) {
  2903.         adjustSize = (extraSpace > 0) ? 1 : -1;
  2904.     }
  2905.     for (partPtr = startPtr; (partPtr < endPtr) && (extraSpace != 0);
  2906.         partPtr++) {
  2907.         availSpace = partPtr->nomSize - partPtr->size;
  2908.         if (((extraSpace > 0) && (availSpace > 0)) ||
  2909.         ((extraSpace < 0) && (availSpace < 0))) {
  2910.         if (ABS(adjustSize) < ABS(availSpace)) {
  2911.             extraSpace -= adjustSize;
  2912.             partPtr->size += adjustSize;
  2913.         } else {
  2914.             extraSpace -= availSpace;
  2915.             partPtr->size += availSpace;
  2916.             numAvail--;
  2917.         }
  2918.         }
  2919.     }
  2920.     }
  2921.  
  2922.     /*
  2923.      *-------------------------------------------------------------------
  2924.      *
  2925.      * Pass 2: Adjust the partitions with space still available
  2926.      *
  2927.      *-------------------------------------------------------------------
  2928.      */
  2929.  
  2930.     numAvail = 0;
  2931.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2932.     if (extraSpace > 0) {
  2933.         availSpace = partPtr->maxSize - partPtr->size;
  2934.     } else {
  2935.         availSpace = partPtr->size - partPtr->minSize;
  2936.     }
  2937.     if (availSpace > 0) {
  2938.         numAvail++;
  2939.     }
  2940.     }
  2941.     while ((numAvail > 0) && (extraSpace != 0)) {
  2942.     adjustSize = extraSpace / numAvail;
  2943.     if (adjustSize == 0) {
  2944.         adjustSize = (extraSpace > 0) ? 1 : -1;
  2945.     }
  2946.     for (partPtr = startPtr; (partPtr < endPtr) && (extraSpace != 0);
  2947.         partPtr++) {
  2948.         if (extraSpace > 0) {
  2949.         availSpace = partPtr->maxSize - partPtr->size;
  2950.         } else {
  2951.         availSpace = partPtr->minSize - partPtr->size;
  2952.         }
  2953.         if (((extraSpace > 0) && (availSpace > 0)) ||
  2954.         ((extraSpace < 0) && (availSpace < 0))) {
  2955.         if (ABS(adjustSize) < ABS(availSpace)) {
  2956.             extraSpace -= adjustSize;
  2957.             partPtr->size += adjustSize;
  2958.         } else {
  2959.             extraSpace -= availSpace;
  2960.             partPtr->size += availSpace;
  2961.             numAvail--;
  2962.         }
  2963.         }
  2964.     }
  2965.     }
  2966. }
  2967.  
  2968. /*
  2969.  *----------------------------------------------------------------------
  2970.  *
  2971.  * ResetPartitions --
  2972.  *
  2973.  *    Sets/resets the size of each row and column partition to the
  2974.  *    minimum limit of the partition (this is usually zero). This
  2975.  *    routine gets called when new slave windows are added, deleted,
  2976.  *    or resized.
  2977.  *
  2978.  * Results:
  2979.  *    None.
  2980.  *
  2981.  * Side Effects:
  2982.  *     The size of each partition is re-initialized to its minimum size.
  2983.  *
  2984.  *----------------------------------------------------------------------
  2985.  */
  2986. static void
  2987. ResetPartitions(partPtr, length)
  2988.     register Partition *partPtr;
  2989.     int length;
  2990. {
  2991.     register int i;
  2992.     int size;
  2993.  
  2994.     for (i = 0; i < length; i++) {
  2995.     if (partPtr->reqSize.nom > 0) {
  2996.         /*
  2997.          * This could be done more cleanly.  Want to ensure that
  2998.          * the requested nominal size is not overridden when
  2999.          * determining the normal sizes.  So temporarily fix min
  3000.          * and max to the nominal size and reset them back later.
  3001.          */
  3002.         partPtr->minSize = partPtr->maxSize = partPtr->size =
  3003.         partPtr->nomSize = partPtr->reqSize.nom;
  3004.  
  3005.     } else {
  3006.         partPtr->minSize = partPtr->reqSize.min;
  3007.         partPtr->maxSize = partPtr->reqSize.max;
  3008.         size = 2 * partPtr->pad;
  3009.         if (size < partPtr->minSize) {
  3010.         size = partPtr->minSize;
  3011.         } else if (size > partPtr->maxSize) {
  3012.         size = partPtr->maxSize;
  3013.         }
  3014.         partPtr->nomSize = 0;
  3015.         partPtr->size = size;
  3016.     }
  3017.     partPtr->span = 0;
  3018.     partPtr++;
  3019.     }
  3020. }
  3021.  
  3022. /*
  3023.  *----------------------------------------------------------------------
  3024.  *
  3025.  * SetNominalSizes
  3026.  *
  3027.  *    Sets the normal sizes for each partition in the array.
  3028.  *    The partition size is the requested window size plus an
  3029.  *    amount of padding.  In addition, adjust the min/max bounds
  3030.  *    of the partition depending upon the resize flags (whether
  3031.  *    the partition can be expanded or shrunk from its normal
  3032.  *    size).
  3033.  *
  3034.  * Results:
  3035.  *    Returns the total space needed for the all the partitions.
  3036.  *
  3037.  * Side Effects:
  3038.  *    The nominal size of each partition in the array is set.
  3039.  *    This is used later to determine how to shrink or grow the
  3040.  *    table if the master window cannot be resized to accommodate
  3041.  *    exactly the size requirements of all the partitions.
  3042.  *
  3043.  *----------------------------------------------------------------------
  3044.  */
  3045. static int
  3046. SetNominalSizes(partPtr, numEntries)
  3047.     register Partition *partPtr;/* Row or column partition array */
  3048.     int numEntries;        /* Number of partitions in the array */
  3049. {
  3050.     register int i, size;
  3051.     int total = 0;
  3052.  
  3053.     for (i = 0; i < numEntries; i++) {
  3054.     /*
  3055.      * Restore the real bounds after setting nominal size.
  3056.      */
  3057.     partPtr->minSize = partPtr->reqSize.min;
  3058.     partPtr->maxSize = partPtr->reqSize.max;
  3059.  
  3060.     size = partPtr->size;
  3061.     if (size > partPtr->maxSize) {
  3062.         size = partPtr->maxSize;
  3063.     }
  3064.     partPtr->nomSize = partPtr->size = size;
  3065.     total += partPtr->nomSize;
  3066.  
  3067.     /*
  3068.      * If a partition can't be resized (to either expand or
  3069.      * shrink), hold its respective limit at its normal size.
  3070.      */
  3071.  
  3072.     if (!(partPtr->resize & RESIZE_EXPAND)) {
  3073.         partPtr->maxSize = partPtr->nomSize;
  3074.     }
  3075.     if (!(partPtr->resize & RESIZE_SHRINK)) {
  3076.         partPtr->minSize = partPtr->nomSize;
  3077.     }
  3078.     partPtr++;
  3079.     }
  3080.     return (total);
  3081. }
  3082.  
  3083. /*
  3084.  *----------------------------------------------------------------------
  3085.  *
  3086.  * LayoutPartitions --
  3087.  *
  3088.  *    Calculates the normal space requirements for both the row
  3089.  *    and column partitions.  Each slave window is added in order of
  3090.  *    the number of rows or columns spanned, which defines the space
  3091.  *    needed among in the partitions spanned.
  3092.  *
  3093.  * Results:
  3094.  *    None.
  3095.  *
  3096.  * Side Effects:
  3097.  *     The sum of normal sizes set here will be used as the normal
  3098.  *    size for the master window.
  3099.  *
  3100.  *----------------------------------------------------------------------
  3101.  */
  3102. static void
  3103. LayoutPartitions(tablePtr)
  3104.     Table *tablePtr;
  3105. {
  3106.     register Blt_ListEntry *entryPtr;
  3107.     register Cubicle *cubiPtr;
  3108.     Partition *rowPtr, *colPtr;
  3109.     int neededSpace, usedSpace, totalSpace;
  3110.     int twiceBW;
  3111.  
  3112.     twiceBW = (2 * Tk_InternalBorderWidth(tablePtr->tkwin));
  3113.     ResetPartitions(tablePtr->colPtr, tablePtr->numCols);
  3114.     for (entryPtr = Blt_FirstListEntry(&(tablePtr->colSorted));
  3115.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  3116.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  3117.     neededSpace = GetReqWidth(cubiPtr) +
  3118.         2 * (cubiPtr->padX + cubiPtr->extBW);
  3119.     if (neededSpace > 0) {
  3120.         colPtr = tablePtr->colPtr + cubiPtr->colIndex;
  3121.         usedSpace = GetSpan(colPtr, cubiPtr->colSpan, WITHOUT_PAD);
  3122.         if (neededSpace > usedSpace) {
  3123.         GrowSpan(colPtr, cubiPtr->colSpan, neededSpace - usedSpace);
  3124.         }
  3125.     }
  3126.     }
  3127.     totalSpace = SetNominalSizes(tablePtr->colPtr, tablePtr->numCols);
  3128.     tablePtr->reqWidth = totalSpace + twiceBW;
  3129.  
  3130.     ResetPartitions(tablePtr->rowPtr, tablePtr->numRows);
  3131.     for (entryPtr = Blt_FirstListEntry(&(tablePtr->rowSorted));
  3132.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  3133.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  3134.     neededSpace = GetReqHeight(cubiPtr) +
  3135.         2 * (cubiPtr->padY + cubiPtr->extBW);
  3136.     if (neededSpace > 0) {
  3137.         rowPtr = tablePtr->rowPtr + cubiPtr->rowIndex;
  3138.         usedSpace = GetSpan(rowPtr, cubiPtr->rowSpan, WITHOUT_PAD);
  3139.         if (neededSpace > usedSpace) {
  3140.         GrowSpan(rowPtr, cubiPtr->rowSpan, neededSpace - usedSpace);
  3141.         }
  3142.     }
  3143.     }
  3144.     totalSpace = SetNominalSizes(tablePtr->rowPtr, tablePtr->numRows);
  3145.     tablePtr->reqHeight = totalSpace + twiceBW;
  3146. }
  3147.  
  3148. /*
  3149.  *----------------------------------------------------------------------
  3150.  *
  3151.  * ArrangeCubicles
  3152.  *
  3153.  *    Places each slave window at its proper location.  First
  3154.  *    determines the size and position of the each window.
  3155.  *    It then considers the following:
  3156.  *
  3157.  *      1. translation of slave window position its parent window.
  3158.  *      2. fill style
  3159.  *      3. anchor
  3160.  *      4. external and internal padding
  3161.  *      5. window size must be greater than zero
  3162.  *
  3163.  * Results:
  3164.  *    None.
  3165.  *
  3166.  * Side Effects:
  3167.  *     The size of each partition is re-initialized its minimum size.
  3168.  *
  3169.  *----------------------------------------------------------------------
  3170.  */
  3171. static void
  3172. ArrangeCubicles(tablePtr)
  3173.     Table *tablePtr;        /* Table widget structure */
  3174. {
  3175.     register Blt_ListEntry *entryPtr;
  3176.     register Cubicle *cubiPtr;
  3177.     register int width, height;
  3178.     Partition *colPtr, *rowPtr;
  3179.     register int x, y;
  3180.     int winWidth, winHeight;
  3181.     int deltaX, deltaY;
  3182.     Tk_Window parent, ancestor;
  3183.     int maxX, maxY;
  3184.  
  3185.     maxX = tablePtr->width - Tk_InternalBorderWidth(tablePtr->tkwin);
  3186.     maxY = tablePtr->height - Tk_InternalBorderWidth(tablePtr->tkwin);
  3187.     for (entryPtr = tablePtr->listPtr->tailPtr; entryPtr != NULL;
  3188.     entryPtr = entryPtr->prevPtr) {
  3189.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  3190.  
  3191.     colPtr = tablePtr->colPtr + cubiPtr->colIndex;
  3192.     rowPtr = tablePtr->rowPtr + cubiPtr->rowIndex;
  3193.  
  3194.     x = colPtr->offset + colPtr->pad + cubiPtr->padX + cubiPtr->extBW;
  3195.     y = rowPtr->offset + rowPtr->pad + cubiPtr->padY + cubiPtr->extBW;
  3196.  
  3197.     /*
  3198.      * Unmap any slave windows which start outside of the master
  3199.      * window
  3200.      */
  3201.     if ((x >= maxX) || (y >= maxY)) {
  3202.         if (Tk_IsMapped(cubiPtr->tkwin)) {
  3203.         Tk_UnmapWindow(cubiPtr->tkwin);
  3204.         }
  3205.         continue;
  3206.     }
  3207.     width = GetSpan(colPtr, cubiPtr->colSpan, WITHOUT_PAD) -
  3208.         (2 * (cubiPtr->padX + cubiPtr->extBW));
  3209.     height = GetSpan(rowPtr, cubiPtr->rowSpan, WITHOUT_PAD) -
  3210.         (2 * (cubiPtr->padY + cubiPtr->extBW));
  3211.  
  3212.     winWidth = GetReqWidth(cubiPtr);
  3213.     winHeight = GetReqHeight(cubiPtr);
  3214.  
  3215.     /*
  3216.      *
  3217.      * Compare the window's requested size to the size of the
  3218.      * span.
  3219.      *
  3220.      * 1) If it's bigger or if the fill flag is set, make them
  3221.      *    the same size. Check that the new size is within the
  3222.      *    bounds set for the window.
  3223.      *
  3224.      * 2) Otherwise, position the window in the space according
  3225.      *    to its anchor.
  3226.      *
  3227.      */
  3228.  
  3229.     if ((width <= winWidth) || (cubiPtr->fill & FILL_X)) {
  3230.         winWidth = width;
  3231.         if (winWidth > cubiPtr->reqWidth.max) {
  3232.         winWidth = cubiPtr->reqWidth.max;
  3233.         }
  3234.     }
  3235.     if ((height <= winHeight) || (cubiPtr->fill & FILL_Y)) {
  3236.         winHeight = height;
  3237.         if (winHeight > cubiPtr->reqHeight.max) {
  3238.         winHeight = cubiPtr->reqHeight.max;
  3239.         }
  3240.     }
  3241.     /*
  3242.      * If the window is too small to be interesting (i.e. it has
  3243.      * only an external border) then unmap it.
  3244.      */
  3245.     if ((winWidth < 1) || (winHeight < 1)) {
  3246.         if (Tk_IsMapped(cubiPtr->tkwin)) {
  3247.         Tk_UnmapWindow(cubiPtr->tkwin);
  3248.         }
  3249.         continue;
  3250.     }
  3251.     deltaX = deltaY = 0;
  3252.     if (width > winWidth) {
  3253.         deltaX = (width - winWidth);
  3254.     }
  3255.     if (height > winHeight) {
  3256.         deltaY = (height - winHeight);
  3257.     }
  3258.     if ((deltaX > 0) || (deltaY > 0)) {
  3259.         XPoint newPt;
  3260.  
  3261.         newPt = TranslateAnchor(deltaX, deltaY, cubiPtr->anchor);
  3262.         x += newPt.x, y += newPt.y;
  3263.     }
  3264.     /*
  3265.      * Clip the slave window at the bottom and/or right edge of
  3266.      * the master
  3267.      */
  3268.     if (winWidth > (maxX - x)) {
  3269.         winWidth = (maxX - x);
  3270.     }
  3271.     if (winHeight > (maxY - y)) {
  3272.         winHeight = (maxY - y);
  3273.     }
  3274.     /*
  3275.      * If the slave's parent window is not the master window,
  3276.      * translate the master window's x and y coordinates to the
  3277.      * coordinate system of the slave's parent.
  3278.      */
  3279.  
  3280.     parent = Tk_Parent(cubiPtr->tkwin);
  3281.     for (ancestor = tablePtr->tkwin; ancestor != parent;
  3282.         ancestor = Tk_Parent(ancestor)) {
  3283.         x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  3284.         y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  3285.     }
  3286.  
  3287.     /*
  3288.      * Resize or move the window if necessary. Save the window's x
  3289.      * and y coordinates for reference next time.
  3290.      */
  3291.  
  3292.     if ((x != cubiPtr->x) || (y != cubiPtr->y) ||
  3293.         (winWidth != Tk_Width(cubiPtr->tkwin)) ||
  3294.         (winHeight != Tk_Height(cubiPtr->tkwin))) {
  3295. #ifdef notdef
  3296.         fprintf(stderr, "%s at %d,%d is %dx%d\n",
  3297.         Tk_PathName(cubiPtr->tkwin), x, y, winWidth, winHeight);
  3298. #endif
  3299.         Tk_MoveResizeWindow(cubiPtr->tkwin, x, y, (unsigned int)winWidth,
  3300.         (unsigned int)winHeight);
  3301.         cubiPtr->x = x, cubiPtr->y = y;
  3302.     }
  3303.     if (!Tk_IsMapped(cubiPtr->tkwin)) {
  3304.         Tk_MapWindow(cubiPtr->tkwin);
  3305.     }
  3306.     }
  3307. }
  3308.  
  3309. /*
  3310.  *----------------------------------------------------------------------
  3311.  *
  3312.  * ArrangeTable --
  3313.  *
  3314.  *
  3315.  * Results:
  3316.  *    None.
  3317.  *
  3318.  * Side Effects:
  3319.  *     The slave windows in the table are possibly resized and redrawn.
  3320.  *
  3321.  *----------------------------------------------------------------------
  3322.  */
  3323. static void
  3324. ArrangeTable(clientData)
  3325.     ClientData clientData;
  3326. {
  3327.     Table *tablePtr = (Table *)clientData;
  3328.     int width, height;
  3329.     register int i;
  3330.     unsigned int offset;
  3331.     int twiceBW;
  3332.  
  3333. #ifdef notdef
  3334.     fprintf(stderr, "ArrangeTable(%s)\n", Tk_PathName(tablePtr->tkwin));
  3335. #endif
  3336.     Tk_Preserve((ClientData)tablePtr);
  3337.     tablePtr->flags &= ~ARRANGE_PENDING;
  3338.  
  3339.     /*
  3340.      * If the table has no children anymore, then don't do anything at
  3341.      * all: just leave the master window's size as-is.
  3342.      */
  3343.  
  3344.     if ((Blt_GetListLength(tablePtr->listPtr) == 0) ||
  3345.     (tablePtr->tkwin == NULL)) {
  3346.     Tk_Release((ClientData)tablePtr);
  3347.     return;
  3348.     }
  3349.     if (tablePtr->flags & REQUEST_LAYOUT) {
  3350.     tablePtr->flags &= ~REQUEST_LAYOUT;
  3351.     LayoutPartitions(tablePtr);
  3352.     }
  3353.     /*
  3354.      * Initially, try to fit the partitions exactly into the master
  3355.      * window by resizing the window.  If the window's requested size
  3356.      * is different, send a request to the master window's geometry
  3357.      * manager to resize.
  3358.      */
  3359.  
  3360.     if ((tablePtr->reqWidth != Tk_ReqWidth(tablePtr->tkwin)) ||
  3361.     (tablePtr->reqHeight != Tk_ReqHeight(tablePtr->tkwin))) {
  3362.     Tk_GeometryRequest(tablePtr->tkwin, tablePtr->reqWidth,
  3363.         tablePtr->reqHeight);
  3364.     tablePtr->flags |= ARRANGE_PENDING;
  3365.     Tk_DoWhenIdle(ArrangeTable, (ClientData)tablePtr);
  3366.     Tk_Release((ClientData)tablePtr);
  3367.     return;
  3368.     }
  3369.     /*
  3370.      * If the parent isn't mapped then don't do anything more: wait
  3371.      * until it gets mapped again.  Need to get at least to here to
  3372.      * reflect size needs up the window hierarchy, but there's no
  3373.      * point in actually mapping the children.
  3374.      */
  3375.  
  3376.     if (!Tk_IsMapped(tablePtr->tkwin)) {
  3377.     Tk_Release((ClientData)tablePtr);
  3378.     return;
  3379.     }
  3380.     /*
  3381.      * Save the width and height of the master window so we know when
  3382.      * it has changed during ConfigureNotify events.
  3383.      */
  3384.  
  3385.     tablePtr->width = Tk_Width(tablePtr->tkwin);
  3386.     tablePtr->height = Tk_Height(tablePtr->tkwin);
  3387.     twiceBW = (2 * Tk_InternalBorderWidth(tablePtr->tkwin));
  3388.  
  3389.     width = GetSpan(tablePtr->colPtr, tablePtr->numCols, WITH_PAD) + twiceBW;
  3390.     height = GetSpan(tablePtr->rowPtr, tablePtr->numRows, WITH_PAD) + twiceBW;
  3391.  
  3392.     /*
  3393.      * If the previous geometry request was not fulfilled (i.e. the
  3394.      * size of the master window is different from partitions' space
  3395.      * requirements), try to adjust size of the partitions to fit the
  3396.      * window.
  3397.      */
  3398.  
  3399.     if (tablePtr->width != width) {
  3400.     AdjustPartitions(tablePtr->colPtr, tablePtr->numCols,
  3401.         tablePtr->width - width);
  3402.     width = GetSpan(tablePtr->colPtr, tablePtr->numCols, WITH_PAD) +
  3403.         twiceBW;
  3404.     }
  3405.     if (tablePtr->height != height) {
  3406.     AdjustPartitions(tablePtr->rowPtr, tablePtr->numRows,
  3407.         tablePtr->height - height);
  3408.     height = GetSpan(tablePtr->rowPtr, tablePtr->numRows, WITH_PAD) +
  3409.         twiceBW;
  3410.     }
  3411.     /*
  3412.      * If after adjusting the size of the partitions the space
  3413.      * required does not equal the size of the window, do one of the
  3414.      * following:
  3415.      *
  3416.      * 1) If is smaller, center the table in the window.
  3417.      * 2) If it's still bigger, clip the partitions that extend beyond
  3418.      *    the edge of the master window.
  3419.      *
  3420.      * Set the row and column offsets (including the master's internal
  3421.      * border width). To be used later when positioning the slave
  3422.      * windows.
  3423.      */
  3424.  
  3425.     offset = Tk_InternalBorderWidth(tablePtr->tkwin);
  3426.     if (width < tablePtr->width) {
  3427.     offset += (tablePtr->width - width) / 2;
  3428.     }
  3429.     for (i = 0; i < tablePtr->numCols; i++) {
  3430.     tablePtr->colPtr[i].offset = offset;
  3431.     offset += tablePtr->colPtr[i].size;
  3432.     }
  3433.  
  3434.     offset = Tk_InternalBorderWidth(tablePtr->tkwin);
  3435.     if (height < tablePtr->height) {
  3436.     offset += (tablePtr->height - height) / 2;
  3437.     }
  3438.     for (i = 0; i < tablePtr->numRows; i++) {
  3439.     tablePtr->rowPtr[i].offset = offset;
  3440.     offset += tablePtr->rowPtr[i].size;
  3441.     }
  3442.     ArrangeCubicles(tablePtr);
  3443.     Tk_Release((ClientData)tablePtr);
  3444. }
  3445.  
  3446. /*
  3447.  *--------------------------------------------------------------
  3448.  *
  3449.  * PartitionCmd --
  3450.  *
  3451.  *    This procedure is invoked to process the Tcl command
  3452.  *    that corresponds to the table geometry manager. It handles
  3453.  *    only those commands related to the table's rows or columns.
  3454.  *    See the user documentation for details on what it does.
  3455.  *
  3456.  * Results:
  3457.  *    A standard Tcl result.
  3458.  *
  3459.  * Side effects:
  3460.  *    See the user documentation.
  3461.  *
  3462.  *--------------------------------------------------------------
  3463.  */
  3464. static int
  3465. PartitionCmd(tablePtr, interp, type, argc, argv)
  3466.     Table *tablePtr;
  3467.     Tcl_Interp *interp;
  3468.     PartitionTypes type;
  3469.     int argc;
  3470.     char **argv;
  3471. {
  3472.     char c;
  3473.     int length;
  3474.     int result = TCL_ERROR;
  3475.     char *partClass;
  3476.  
  3477.     partClass = partitionNames[(int)type];
  3478.     if (argc < 4) {
  3479.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3480.         " master ", partClass, " option number ?args?", (char *)NULL);
  3481.     return TCL_ERROR;
  3482.     }
  3483.     c = argv[3][0];
  3484.     length = strlen(argv[3]);
  3485.     if ((c == 'c') && (strncmp(argv[3], "configure", length) == 0)) {
  3486.     if (argc < 5) {
  3487.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3488.         " master ", partClass, " configure index", (char *)NULL);
  3489.         return TCL_ERROR;
  3490.     }
  3491.     result = ConfigurePartition(tablePtr, interp, type, argc - 4, argv + 4);
  3492.     } else if ((c == 'd') && (strncmp(argv[3], "dimension", length) == 0)) {
  3493.     if (argc != 4) {
  3494.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3495.         " master ", partClass, " dimension", (char *)NULL);
  3496.         return TCL_ERROR;
  3497.     }
  3498.     sprintf(interp->result, "%d", NumEntries(tablePtr, type));
  3499.     result = TCL_OK;
  3500.     } else if ((c == 'i') && (strncmp(argv[3], "info", length) == 0)) {
  3501.     if (argc < 5) {
  3502.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3503.         " master ", partClass, " info index", (char *)NULL);
  3504.         return TCL_ERROR;
  3505.     }
  3506.     result = PartitionInfo(tablePtr, interp, type, argc - 4, argv + 4);
  3507.     } else if ((c == 's') && (strncmp(argv[3], "sizes", length) == 0)) {
  3508.     if (argc != 5) {
  3509.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3510.         " master ", partClass, " sizes index", (char *)NULL);
  3511.         return TCL_ERROR;
  3512.     }
  3513.     result = PartitionSizes(tablePtr, interp, type, argv[4]);
  3514.     } else if ((c == 'l') && (strncmp(argv[3], "location", length) == 0)) {
  3515.     if (argc != 5) {
  3516.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3517.         " master ", partClass, " location coord", (char *)NULL);
  3518.         return TCL_ERROR;
  3519.     }
  3520.     result = LocationInfo(tablePtr, interp, type, argv[4]);
  3521.     } else {
  3522.     Tcl_AppendResult(interp, "unknown ", partClass, " option \"", argv[3],
  3523.         "\": should be configure, dimension, info, location, or sizes",
  3524.         (char *)NULL);
  3525.     }
  3526.     return result;
  3527. }
  3528.  
  3529. /*
  3530.  *--------------------------------------------------------------
  3531.  *
  3532.  * TableCmd --
  3533.  *
  3534.  *    This procedure is invoked to process the Tcl command
  3535.  *    that corresponds to the table geometry manager.
  3536.  *    See the user documentation for details on what it does.
  3537.  *
  3538.  * Results:
  3539.  *    A standard Tcl result.
  3540.  *
  3541.  * Side effects:
  3542.  *    See the user documentation.
  3543.  *
  3544.  *--------------------------------------------------------------
  3545.  */
  3546. static int
  3547. TableCmd(clientData, interp, argc, argv)
  3548.     ClientData clientData;
  3549.     Tcl_Interp *interp;
  3550.     int argc;
  3551.     char **argv;
  3552. {
  3553.     char c;
  3554.     int length;
  3555.     int result = TCL_ERROR;
  3556.     Tk_Window mainWindow = (Tk_Window)clientData;
  3557.     Table *tablePtr;
  3558.  
  3559.     if (argc < 2) {
  3560.     Tcl_AppendResult(interp,
  3561.         "wrong # args: should be \"", argv[0], " options\"", (char *)NULL);
  3562.     return TCL_ERROR;
  3563.     }
  3564.     /* Initialize structures managing all table widgets once. */
  3565.  
  3566.     if (!initialized) {
  3567.     Tcl_InitHashTable(&masterWindows, TCL_ONE_WORD_KEYS);
  3568.     Tcl_InitHashTable(&slaveWindows, TCL_ONE_WORD_KEYS);
  3569.     initialized = TRUE;
  3570.     }
  3571.     tablePtr = NULL;
  3572.     c = argv[1][0];
  3573.     length = strlen(argv[1]);
  3574.     if (c == '.') {
  3575.     tablePtr = FindTable(interp, argv[1], mainWindow, 0);
  3576.  
  3577.     /* If the table doesn't exist, create one. */
  3578.     if (tablePtr == NULL) {
  3579.         tablePtr = CreateTable(interp, argv[1], mainWindow);
  3580.         if (tablePtr == NULL) {
  3581.         return TCL_ERROR;
  3582.         }
  3583.     }
  3584.     return (ManageWindows(tablePtr, interp, argc - 2, argv + 2));
  3585.     } else if ((c == 'c') && (length > 1) &&
  3586.     (strncmp(argv[1], "configure", length) == 0)) {
  3587.     if (argc < 3) {
  3588.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3589.         " configure slave ?option-values ...?\"", (char *)NULL);
  3590.         return TCL_ERROR;
  3591.     }
  3592.     return (ConfigureCubicle(clientData, interp, argc - 2, argv + 2));
  3593.     } else if ((c == 'c') && (length > 1) &&
  3594.            (strncmp(argv[1], "cget", length) == 0)) {
  3595.         if (argc != 4 || strncmp(argv[2], "rows", strlen(argv[2])) ||
  3596.         strncmp(argv[2], "columns", strlen(argv[2]))) {
  3597.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  3598.                     argv[0], " cget rows/columns option\"", (char *) NULL);
  3599.         return TCL_ERROR;
  3600.         }
  3601.     if (argv[2][0] == 'r') {
  3602.         result = Tk_ConfigureValue(interp, tablePtr->tkwin, rowConfigSpecs,
  3603.                        (char *) tablePtr, argv[2], 0);
  3604.     } else {
  3605.         result = Tk_ConfigureValue(interp, tablePtr->tkwin, columnConfigSpecs,
  3606.                        (char *) tablePtr, argv[2], 0);
  3607.     }
  3608.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  3609.     if (argc < 3) {
  3610.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3611.         " forget slave ?slave ...?\"", (char *)NULL);
  3612.         return TCL_ERROR;
  3613.     }
  3614.     return (ForgetWindow(clientData, interp, argc - 2, argv + 2));
  3615.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  3616.     if (argc != 3) {
  3617.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3618.         " info slave\"", (char *)NULL);
  3619.         return TCL_ERROR;
  3620.     }
  3621.     return (SlaveInfo(clientData, interp, argv[2]));
  3622.     }
  3623.     /*
  3624.      * The rest of the options all take the pathname of the master
  3625.      * window as their second argument.  If the second argument starts
  3626.      * with a ".", try to find the table associated with it.
  3627.      */
  3628.     if ((argc > 2) /* && (argv[2][0] == '.') */) {    /* SAU */
  3629.     tablePtr = FindTable(interp, argv[2], mainWindow, TCL_LEAVE_ERR_MSG);
  3630.     if (tablePtr == NULL) {
  3631.         return TCL_ERROR;
  3632.     }
  3633.     }
  3634.     if ((c == 'c') && (length > 2) &&
  3635.     (strncmp(argv[1], "column", length) == 0)) {
  3636.     result = PartitionCmd(tablePtr, interp, COLUMN_PARTITION_TYPE, argc,
  3637.         argv);
  3638.     } else if ((c == 'r') && (strncmp(argv[1], "row", length) == 0)) {
  3639.     result = PartitionCmd(tablePtr, interp, ROW_PARTITION_TYPE, argc,
  3640.         argv);
  3641.     } else if ((c == 'm') && (strncmp(argv[1], "masters", length) == 0)) {
  3642.     if ((argc != 2) && (argc != 3)) {
  3643.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3644.         " masters ?pattern?", (char *)NULL);
  3645.         return TCL_ERROR;
  3646.     }
  3647.     result = MasterNames(clientData, interp, argc - 2, argv + 2);
  3648.     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
  3649.     if (argc < 3) {
  3650.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3651.         " slaves master ?options?", (char *)NULL);
  3652.         return TCL_ERROR;
  3653.     }
  3654.     result = SlaveNames(tablePtr, interp, argc - 2, argv + 2);
  3655.     } else if ((c == 'a') && (strncmp(argv[1], "arrange", length) == 0)) {
  3656.     if (argc != 3) {
  3657.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3658.         " arrange master", (char *)NULL);
  3659.         return TCL_ERROR;
  3660.     }
  3661.     result = LayoutTable(tablePtr);
  3662.     } else {
  3663.     Tcl_AppendResult(interp, "unknown option \"", argv[1], "\": should be\
  3664.  arrange, configure, column, forget, info, masters, row, or slaves", (char *)NULL);
  3665.     }
  3666.     return result;
  3667. }
  3668.  
  3669. /*
  3670.  *--------------------------------------------------------------
  3671.  *
  3672.  * Blt_TableInit --
  3673.  *
  3674.  *    This procedure is invoked to initialized the Tcl command
  3675.  *    that corresponds to the table geometry manager.
  3676.  *
  3677.  * Results:
  3678.  *    None.
  3679.  *
  3680.  * Side effects:
  3681.  *    Creates the new command and adds an entry into a global
  3682.  *    Tcl associative array.
  3683.  *
  3684.  *--------------------------------------------------------------
  3685.  */
  3686. int
  3687. Blt_TableInit(interp)
  3688.     Tcl_Interp *interp;
  3689. {
  3690.     Tk_Window tkwin;
  3691.  
  3692.     if (Blt_FindCmd(interp, "blt_table", (ClientData *)NULL) == TCL_OK) {
  3693.     Tcl_AppendResult(interp, "\"blt_table\" command already exists",
  3694.         (char *)NULL);
  3695.     return TCL_ERROR;
  3696.     }
  3697.     tkwin = Tk_MainWindow(interp);
  3698.     if (tkwin == NULL) {
  3699.     Tcl_AppendResult(interp, "\"blt_table\" requires Tk", (char *)NULL);
  3700.     return TCL_ERROR;
  3701.     }
  3702.     Tcl_SetVar2(interp, "blt_versions", "blt_table", TABLE_VERSION,
  3703.     TCL_GLOBAL_ONLY);
  3704.     Tcl_CreateCommand(interp, "blt_table", TableCmd, (ClientData)tkwin,
  3705.     (Tcl_CmdDeleteProc *)NULL);
  3706.     return TCL_OK;
  3707. }
  3708.  
  3709. /* find the maximum *used* row or column entry in a table 
  3710.  * SAU
  3711.  * This is probably wrong, but its better that what was there
  3712.  * before
  3713.  */
  3714.  
  3715. static int
  3716. NumEntries (tablePtr, type)
  3717.     Table *tablePtr;
  3718.     PartitionTypes type;
  3719. {
  3720.     int max = 0;
  3721.     int last;
  3722.     Blt_ListEntry *entryPtr;
  3723.     Blt_LinkedList *listPtr;
  3724.     Cubicle *cubiPtr;
  3725.  
  3726.     if (type == ROW_PARTITION_TYPE) {
  3727.         listPtr = &(tablePtr->rowSorted);
  3728.     } else {
  3729.         listPtr = &(tablePtr->colSorted);
  3730.     }
  3731.     for (entryPtr = Blt_FirstListEntry(listPtr);
  3732.             entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  3733.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  3734.     if (type == ROW_PARTITION_TYPE) {
  3735.         last = cubiPtr->rowSpan + cubiPtr->rowIndex;
  3736.     } else {
  3737.         last = cubiPtr->colSpan + cubiPtr->colIndex;
  3738.     }
  3739. /*
  3740.     fprintf(stderr," %d,%d (%d %d) <%d <= %d>\n",
  3741.         cubiPtr->rowIndex, cubiPtr->colIndex,
  3742.         cubiPtr->rowSpan, cubiPtr->colSpan,
  3743.         max,last);
  3744. */
  3745.     if (last > max) max = last;
  3746.     }
  3747.     return(max);
  3748. }
  3749.